![clockface.jpg](https://cdn-learn.adafruit.com/user_assets/assets/000/000/278/large1024/clockface.jpg?1698954664)
This is my take on an itty bitty bed-side clock, for the side of my bed. (where else?)
I want it to show the current time, date, and outside temperature with a general weather icon for the hour. I will be using (and modifying) any code on the Adafruit site. The clock auto dims at local sunset, and returns to daytime brightness at local sunrise
Now all I need is a suitable case. I plan to run this off a USB power dongle instead of battery.
Any USB wall-wart should work.
Here's the code:
# SPDX-FileCopyrightText: 2023 Trevor Beaton for Adafruit Industries # Version: 6 # SPDX-License-Identifier: MIT # # Phisical change: add a wire from D12 to RESET for auto-restart on no internet comunication. import os import ssl import time import wifi import board import displayio import digitalio import terminalio import socketpool import adafruit_requests import adafruit_imageload import neopixel import math from io import BytesIO from adafruit_display_text import bitmap_label from adafruit_datetime import date from digitalio import DigitalInOut # Initialize Wi-Fi connection try: wifi.radio.connect( os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD") ) print("Connected to %s!" % os.getenv("CIRCUITPY_WIFI_SSID")) except Exception as e: # pylint: disable=broad-except print( "Failed to connect to WiFi. Error:", e, "\nBoard will hard reset in 30 seconds." ) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # initalize global vareabels preveous_day = 0 preveous_hour = 0 preveous_minute = -1 start = 0 backlight = 1.0 force_weather_update = True force_date_update = True top_count = 0 middel_count = 0 bottom_count = 0 # Set up the neopixel pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) pixel.brightness = 0.7 # Set up the URL for fetching time data DATA_SOURCE_TIME = "http://worldtimeapi.org/api/timezone/" + os.getenv("TIMEZONE") DATA_SOURCE_WEATHER = "https://api.weather.gov/stations/KSAC/observations/latest" # Set up display a default image display = board.DISPLAY display.brightness = backlight current_background_image = "/images/blank.bmp" default_bitmap = displayio.OnDiskBitmap(current_background_image) default_tile_grid = displayio.TileGrid(default_bitmap, pixel_shader=default_bitmap.pixel_shader) # initalize the display grid group = displayio.Group() group.append(default_tile_grid) # Set up brighness control buttons middel_button = digitalio.DigitalInOut(board.D1) bottom_button = digitalio.DigitalInOut(board.D2) top_button = digitalio.DigitalInOut(board.BUTTON) top_button.pull = digitalio.Pull.UP # Create label for displaying time, date, dow, and weather time_label = bitmap_label.Label(terminalio.FONT, scale=3) time_label.anchor_point = (0.15, 1.90) time_label.anchored_position = (display.width // 2, display.height // 2) date_label = bitmap_label.Label(terminalio.FONT, scale=2) date_label.anchor_point = (0.15, 1.25) date_label.anchored_position = (display.width // 2, display.height // 2) dow_label = bitmap_label.Label(terminalio.FONT, scale=2) dow_label.anchor_point = (0.15, 0.35) dow_label.anchored_position = (display.width // 2, display.height // 2) weather_label = bitmap_label.Label(terminalio.FONT, scale=2) weather_label.anchor_point = (0.45, -1.10) weather_label.anchored_position = (display.width // 2, display.height // 2) # Create main group to hold all display groups main_group = displayio.Group() main_group.append(group) main_group.append(time_label) main_group.append(date_label) main_group.append(dow_label) main_group.append(weather_label) # Show the main group on the display display.show(main_group) def get_temprature(raw_data): temperature = int(0 if raw_data["properties"]["temperature"]["value"] is None else raw_data["properties"]["temperature"]["value"]) temperature = (((temperature * 9) // 5) + 32) + 0.5 int_temperature = math.floor(temperature) return int_temperature def get_wind(raw_data): wind_speed = int(0 if raw_data["properties"]["windSpeed"]["value"] is None else raw_data["properties"]["windSpeed"]["value"]) wind_from = rain_amount = int(0 if raw_data["properties"]["windDirection"]["value"] is None else raw_data["properties"]["windDirection"]["value"]) wind_speed = (wind_speed * 0.621371) + 0.5 int_wind_speed = math.floor(wind_speed) wind_str = "{} @ {}mph".format(wind_from, int_wind_speed) return wind_str def get_sky(raw_data, is_day): sky_condition_str = raw_data["properties"]["cloudLayers"][-1]["amount"] rain_amount = int(0 if raw_data["properties"]["precipitationLastHour"]["value"] is None else raw_data["properties"]["precipitationLastHour"]["value"]) if sky_condition_str == "CLR": # 0 of 8 parts of the sky is cloudy if is_day: set_background_image("/images/clear_D.bmp") else: set_background_image("/images/clear_N.bmp") elif sky_condition_str == "FEW": # 1 to 2 of 8 parts of the sky is cloudy if is_day: set_background_image("/images/prtlycldy_D.bmp") else: set_background_image("/images/prtlycldy_N.bmp") elif sky_condition_str == "SCT": # 3 to 4 of 8 parts of the sky is cloudy if rain_amount > 0: set_background_image("/images/rain.bmp") else: if is_day: set_background_image("/images/prtlycldy_D.bmp") else: set_background_image("/images/prtlycldy_N.bmp") elif sky_condition_str == "BKN": # 5 to 6 of 8 parts of the sky is cloudy if rain_amount > 0: set_background_image("/images/rain.bmp") else: set_background_image("/images/cloudy.bmp") elif sky_condition_str == "OVC": # 7 to 8 of 8 parts of the sky is cloudy & can determin cloud bottom altitude if rain_amount > 0: set_background_image("/images/rain.bmp") else: set_background_image("/images/cloudy.bmp") elif sky_condition_str == "VV": # 8 of 8 parts of the sky is cloudy & cannot determin cloud bottom altitude set_background_image("/images/haze.bmp") else: print("New conditions = " + sky_condition + "\n") set_background_image("/images/blank.bmp") return set_background_image def set_background_image(filename): global current_background_image # pylint: disable=global-statement tile_bitmap = displayio.OnDiskBitmap(filename) new_tile_grid = displayio.TileGrid(tile_bitmap, pixel_shader=tile_bitmap.pixel_shader) group[0] = new_tile_grid current_background_image = filename def parse_time(datetime_str, data_time): # Extract the time part from the datetime string time_str = datetime_str.split("T")[1].split(".")[0] hour, minute, _ = map(int, time_str.split(":")) # Convert 24-hour format to 12-hour format and determine AM/PM period = "AM" if hour >= 12: period = "PM" if hour > 12: hour -= 12 elif hour == 0: hour = 12 # Extract the date part from the datetime string date_str = datetime_str.split("T")[0] year, month, day = (int(x) for x in date_str.split('-')) temp_date = date(year, month, day) date_str = "{}/{}/{}".format(month, day, year) dow = temp_date.weekday() if dow == 0: dow_str = "Monday" elif dow == 1: dow_str = "Tuesday" elif dow == 2: dow_str = "Wednesday" elif dow == 3: dow_str = "Thursday" elif dow == 4: dow_str = "Friday" elif dow == 5: dow_str = "Saturday" else: dow_str = "Sunday" return dow_str, date_str, hour, minute, period, day, month, year # **************************************************************** # *------------------- MAIN LOOP START ------------------------* # **************************************************************** while True: print("\nStart") # Fetch time data from WorldTimeAPI try: response_time = requests.get(DATA_SOURCE_TIME) data_time = response_time.json() except Exception: # wire D12 to RESET for auto-restart to function reset_now = digitalio.DigitalInOut(board.D12) reset_now.direction = digitalio.Direction.OUTPUT reset_now = 0 # Parse the day of week, date, & time from the datetime string current_dow_str,current_date_str,current_hour,current_minute,current_period,current_day,current_month,current_year = parse_time(data_time["datetime"], data_time) # Display the date, if new day or when forced to run, it is froced to run at a fresh start if current_day != preveous_day or force_date_update: date_label.text = current_date_str dow_label.text = current_dow_str DATA_SOURCE_SUNRISESET = "https://api.sunrisesunset.io/json?lat=" + os.getenv("LAT") + "&lng=" + os.getenv("LNG") + "&date={}-{}-{}".format(current_year,current_month,current_day) try: response_sun = requests.get(DATA_SOURCE_SUNRISESET) data_sun = response_sun.json() except Exception: # wire D12 to RESET for auto-restart to function reset_now = digitalio.DigitalInOut(board.D12) reset_now.direction = digitalio.Direction.OUTPUT reset_now = 0 sun_rise = data_sun["results"]["sunrise"] sun_rise_hour = int(sun_rise.split(":")[0]) sun_rise_minute = int(sun_rise.split(":")[1]) sun_rise_period = sun_rise.split(" ")[1] if sun_rise_period == "PM" and not sun_rise_hour == 12: sun_rise_24hour_time = (sun_rise_hour + 12) + (sun_rise_minute / 60) elif sun_rise_period == "AM" and sun_rise_hour == 12: sun_rise_24hour_time = (sun_rise_hour - 12) + (sun_rise_minute / 60) else: sun_rise_24hour_time = sun_rise_hour + (sun_rise_minute / 60) sun_set = data_sun["results"]["sunset"] sun_set_hour = int(sun_set.split(":")[0]) sun_set_minute = int(sun_set.split(":")[1]) sun_set_period = sun_set.split(" ")[1] if sun_set_period == "PM" and not sun_set_hour == 12: sun_set_24hour_time = (sun_set_hour + 12) + (sun_set_minute / 60) elif sun_set_period == "AM" and sun_set_hour == 12: sun_set_24hour_time = (sun_set_hour - 12) + (sun_set_minute / 60) else: sun_set_24hour_time = sun_set_hour + (sun_set_minute / 60) if current_period == "PM" and not current_hour == 12: current_24hour_time = (current_hour + 12) + (current_minute / 60) elif current_period == "AM" and current_hour == 12: current_24hour_time = (current_hour - 12) + (current_minute / 60) else: current_24hour_time = current_hour + (current_minute / 60) # print("sun rise {:2}:{:02} ".format(sun_rise_hour, sun_rise_minute) + sun_rise_period + " {:2}".format(sun_rise_24hour_time)) # print("sun set {:2}:{:02} ".format(sun_set_hour, sun_set_minute) + sun_set_period + " {:2}".format(sun_set_24hour_time)) # print("time {:2}:{:02} ".format(current_hour, current_minute) + current_period + " {:2}".format(current_24hour_time)) if (current_24hour_time >= sun_rise_24hour_time and current_24hour_time < sun_set_24hour_time): is_daytime = True backlight = float(os.getenv("DAY_BRITNESS")) # print("bright") else: is_daytime = False backlight = float(os.getenv("NIGHT_BRITNESS")) # print("dim") display.brightness = backlight force_weather_update = True force_date_update = False # Get new weather data at five minutes past the hour change or when forced to run, it is froced to run at a fresh start if (current_hour != preveous_hour and current_minute == 5 and top_count == 0 and middel_count == 0 and \ bottom_count == 0) or force_weather_update: try: response_weather = requests.get(DATA_SOURCE_WEATHER) data_weather = response_weather.json() except Exception: # wire D12 to RESET for auto-restart to function reset_now = digitalio.DigitalInOut(board.D12) reset_now.direction = digitalio.Direction.OUTPUT reset_now = 0 data_temperature = get_temprature(data_weather) wind_data_str = get_wind(data_weather) data_sky = get_sky(data_weather, is_daytime) string_weather = "{}F W:".format(data_temperature) + wind_data_str weather_label.text = string_weather preveous_hour = current_hour force_weather_update = False # Display the time string_time = "{:2}:{:02} {}".format( current_hour, current_minute, current_period) time_label.text = string_time if not top_button.value: pixel.fill((0,255,0)) time.sleep(0.2) weather_label.text = "Set: " + data_sun["results"]["sunset"] pixel.fill((0,0,0)) top_count = 3 else: if top_count == 0 and middel_count == 0 and bottom_count == 0: weather_label.text = string_weather elif not top_count == 0: top_count -= 1 if middel_button.value: pixel.fill((0,255,0)) time.sleep(0.2) seconds_str = data_time["datetime"].split("T")[1].split(".")[0] seconds_str = seconds_str.split(":")[2] weather_label.text = "Seconds: " + seconds_str pixel.fill((0,0,0)) middel_count = 3 else: if top_count == 0 and middel_count == 0 and bottom_count == 0: weather_label.text = string_weather elif not middel_count == 0: middel_count -= 1 if bottom_button.value: pixel.fill((0,255,0)) time.sleep(0.2) weather_label.text = "Golden: " + data_sun["results"]["golden_hour"] pixel.fill((0,0,0)) bottom_count = 3 else: if top_count == 0 and middel_count == 0 and bottom_count == 0: weather_label.text = string_weather elif not bottom_count == 0: bottom_count -= 1 print("End") time.sleep(2)
.