CircuitPython Code
In the embedded code element below, click on the Download: Project Zip link, and save the .zip archive file to your computer.
Then, uncompress the .zip file, it will unpack to a folder named PyPortal_Wakeup_Light.
Copy the contents of the PyPortal_Wakeup_Light directory to your PyPortal's CIRCUITPY drive.
# SPDX-FileCopyrightText: 2019 Isaac Wellish for Adafruit Industries # # SPDX-License-Identifier: MIT """ This example uses a PyPortal and rgbw leds for a simple "wake up" light. The strip starts to brighten 30 minutes before set wake up time. This program assumes a neopixel strip is attached to D3 on the Adafruit PyPortal. """ import time import board import neopixel from adafruit_pyportal import PyPortal from adafruit_bitmap_font import bitmap_font from adafruit_display_text.Label import Label # type in time to get up each day of the week default_wake_up = "6:30A" up_time_monday = default_wake_up up_time_tuesday = default_wake_up up_time_wednesday = default_wake_up up_time_thursday = default_wake_up up_time_friday = default_wake_up up_time_saturday = "10:00A" up_time_sunday = "10:00A" wake_up_times = (up_time_monday, up_time_tuesday, up_time_wednesday, up_time_thursday, up_time_friday, up_time_saturday, up_time_sunday, default_wake_up) days_str = ("Mon.", "Tues.", "Wed.", "Thurs.", "Fri.", "Sat.", "Sun.") # set neopixel min and max brightness BRIGHTNESS = 0 MIN_BRIGHTNESS = 0 MAX_BRIGHTNESS = 0.85 # initialize neopixel strip num_pixels = 30 ORDER = neopixel.RGBW strip = neopixel.NeoPixel(board.D3, num_pixels, brightness=BRIGHTNESS, pixel_order=ORDER) strip.fill(0) # start it set to off # color of strip WHITE = (0, 0, 0, 255) # number of minutes it takes for strip to fade from min to max light_minutes = 30 # determine the current working directory # needed so we know where to find files cwd = ("/"+__file__).rsplit('/', 1)[0] # initialize the pyportal object and let us know what data to fetch and where # to display it pyportal = PyPortal(status_neopixel=board.NEOPIXEL, default_bg=0x000000) # set backlight default to off backlight_off = 0 backlight_on = 0.8 pyportal.set_backlight(backlight_off) # assign fonts big_font = bitmap_font.load_font(cwd+"/fonts/Nunito-Light-75.bdf") big_font.load_glyphs(b'0123456789:AP') # pre-load glyphs for fast printing print('loading fonts...') info_font = bitmap_font.load_font(cwd+"/fonts/Nunito-Black-17.bdf") info_font.load_glyphs(b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.:/ ') time_color = 0xFFFFFF time_position = (75,130) time_textarea = Label(big_font, color=time_color, x=time_position[0], y=time_position[1]) wakeup_time_color = 0xFFFFFF wakeup_time_position = (15,200) wakeup_time_textarea = Label(info_font, color=wakeup_time_color, x=wakeup_time_position[0], y=wakeup_time_position[1]) light_on_time_color = 0xFFFFFF light_on_time_position = (15,220) light_on_time_textarea = Label(info_font, color=light_on_time_color, x=light_on_time_position[0], y=light_on_time_position[1]) pyportal.splash.append(time_textarea) pyportal.splash.append(wakeup_time_textarea) pyportal.splash.append(light_on_time_textarea) while True: try: print("Getting time from internet!") pyportal.get_local_time() except RuntimeError as e: print("Some error occured, retrying! -", e) continue break # parse given time string into hour minute and AM_PM elements def parseTime(time_before): hours_before, minutes_before = time_before.split(":") AM_PM_str = minutes_before[-1:] minutes_before = int(minutes_before[:-1]) if (hours_before != '12') and AM_PM_str == 'P': hours_before = int(hours_before) + 12 elif ((hours_before == '12') and (AM_PM_str == 'P')): hours_before = int(hours_before) elif ((hours_before == '12') and (AM_PM_str == 'A')): hours_before = 0 else: hours_before = int(hours_before) parsed_time = [hours_before, minutes_before] return parsed_time # get time objects for wake up times val_times = [] parsed_times = [] for i in range(len(wake_up_times)): parsed_time_day = parseTime(wake_up_times[i]) hours, minutes = parsed_time_day[0:2] now_day = time.localtime() time_obj_mk = time.mktime((now_day[0], now_day[1], now_day[2], hours, minutes, now_day[5], i, now_day[7], now_day[8])) time_obj = time.localtime(time_obj_mk) val_times.append(time_obj_mk) parsed_times.append(time_obj) # determine which day it is and print which time waking up on screen def whichDay(): now = time.localtime() current_day = now[6] now_mk = time.mktime((now[0], now[1], now[2], now[3], now[4], now[5], now[6], now[7], now[8])) # if it's after midnight and before todays wakeup time, display the wake up time of today for day in range(len(wake_up_times)): if now_mk < val_times[day]: if current_day == day: input_wake_up_time = wake_up_times[day] use_day = day # set wake up time to the next day's wake up time after current day's wake up time else: if current_day == 6: input_wake_up_time = wake_up_times[0] use_day = 0 else: if current_day == day: input_wake_up_time = wake_up_times[day+1] use_day = day + 1 input_wake_up_time_text = "Wake up " + days_str[use_day] + " at " + input_wake_up_time wakeup_time_textarea.text = input_wake_up_time_text return use_day def displayTime(): now = time.localtime() hour, minute = now[3:5] print(now) print("Current time: %02d:%02d" % (hour, minute)) formatTime(hour, minute) time_textarea.text = formatTime(hour, minute) return formatTime(hour, minute) def formatTime(raw_hours, raw_minutes): # display the time in a nice big font format_str = "%d:%02d" if raw_hours >= 12: raw_hours -= 12 format_str = format_str+"P" else: format_str = format_str+"A" if raw_hours == 0: raw_hours = 12 time_str = format_str % (raw_hours, raw_minutes) return time_str def backLight(): now = time.localtime() now_val = time.mktime((now[0], now[1], now[2], now[3], now[4], now[5], now[6], now[7], now[8])) wake_up_day_val = val_times[now[6]] # if time is more than 9 hours after current day's wake up time, # or time is before light start time, backlight off, tap to turn on if (now_val - wake_up_day_val) > 32400 or (now_val - wake_up_day_val) < -1800: pyportal.set_backlight(backlight_off) if pyportal.touchscreen.touch_point: pyportal.set_backlight(backlight_on) time.sleep(5) pyportal.set_backlight(backlight_off) else: pyportal.set_backlight(backlight_on) def subtract30min(day): # subtract 30 min # get the time object from the corresponding day raw_wake_up_time = parsed_times[day] now = time.localtime() # new time subtracting 30 min from wake up time minus30 = time.mktime((now[0], now[1], now[2], raw_wake_up_time[3], raw_wake_up_time[4] - 30, now[5], now[6], now[7], now[8])) time_minus30 = time.localtime(minus30) hour_minus30 = time_minus30[3] minutes_minus30 = time_minus30[4] light_on_time_textarea.text = "Light starting at: " + formatTime(hour_minus30, minutes_minus30) return formatTime(hour_minus30, minutes_minus30) refresh_time = None while True: time_now = time.localtime() # only query the online time once per hour (and on first run) if (not refresh_time) or (time.monotonic() - refresh_time) > 3600: try: print("Getting time from internet!") pyportal.get_local_time() refresh_time = time.monotonic() except RuntimeError as e: print("Some error occured, retrying! -", e) continue time_str_text = displayTime() print(time_str_text) # determine which wake up time to choose based on the day wake_up_day = whichDay() # if time is more than 9 hours after previous day's wake up time, # backlight off and can tap to turn on backLight() # start the light 30 min before wake up time start_light_time = subtract30min(wake_up_day) # If current day is same as wake up day and # wake up time - 30 minutes equals current time, start the light if wake_up_day == time_now[6] and time_str_text == start_light_time: print("Starting wake up light") # turn on backlight pyportal.set_backlight(backlight_on) for i in range(light_minutes - 1): BRIGHTNESS = BRIGHTNESS + (MAX_BRIGHTNESS/light_minutes) # max 0.25, min 0.0 strip.fill(WHITE) strip.brightness = BRIGHTNESS displayTime() time.sleep(60) # 60 for once per min while not pyportal.touchscreen.touch_point: # turn strip off displayTime() time.sleep(1) continue strip.brightness = MIN_BRIGHTNESS # update every second so that screen can be tapped to view time time.sleep(1)
This project uses the following CircuitPython libraries:
- adafruit_bitmap_font (directory)
- adafruit_bus_device (directory)
- adafruit_display_shapes (directory)
- adafruit_display_text (directory)
- adafruit_esp32spi (directory)
- adafruit_imageload (directory)
- adafruit_io (directory)
- adafruit_pyportal.mpy (file)
- adafruit_requests (file)
- adafruit_touchscreen.mpy (file)
- neopixel.mpy (file)
This is what the final contents of the CIRCUITPY drive will look like:
