code.py
  1  # SPDX-FileCopyrightText: 2019 Isaac Wellish for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  """
  6  This example uses a PyPortal and rgbw leds for a simple "wake up" light.
  7  The strip starts to brighten 30 minutes before set wake up time.
  8  This program assumes a neopixel strip is attached to D3 on the Adafruit PyPortal.
  9  """
 10  import time
 11  import board
 12  import neopixel
 13  from adafruit_pyportal import PyPortal
 14  from adafruit_bitmap_font import bitmap_font
 15  from adafruit_display_text.Label import Label
 16  
 17  # type in time to get up  each day of the week
 18  default_wake_up = "6:30A"
 19  up_time_monday = default_wake_up
 20  up_time_tuesday = default_wake_up
 21  up_time_wednesday = default_wake_up
 22  up_time_thursday = default_wake_up
 23  up_time_friday = default_wake_up
 24  up_time_saturday = "10:00A"
 25  up_time_sunday = "10:00A"
 26  wake_up_times = (up_time_monday,
 27                   up_time_tuesday,
 28                   up_time_wednesday,
 29                   up_time_thursday,
 30                   up_time_friday,
 31                   up_time_saturday,
 32                   up_time_sunday,
 33                   default_wake_up)
 34  days_str = ("Mon.", "Tues.", "Wed.", "Thurs.", "Fri.", "Sat.", "Sun.")
 35  
 36  # set neopixel min and max brightness
 37  BRIGHTNESS = 0
 38  MIN_BRIGHTNESS = 0
 39  MAX_BRIGHTNESS = 0.85
 40  # initialize neopixel strip
 41  num_pixels = 30
 42  ORDER = neopixel.RGBW
 43  strip = neopixel.NeoPixel(board.D3, num_pixels, brightness=BRIGHTNESS,
 44                            pixel_order=ORDER)
 45  strip.fill(0) # start it set to off
 46  # color of strip
 47  WHITE = (0, 0, 0, 255)
 48  # number of minutes it takes for strip to fade from min to max
 49  light_minutes = 30
 50  
 51  # determine the current working directory
 52  # needed so we know where to find files
 53  cwd = ("/"+__file__).rsplit('/', 1)[0]
 54  
 55  # initialize the pyportal object and let us know what data to fetch and where
 56  # to display it
 57  pyportal = PyPortal(status_neopixel=board.NEOPIXEL,
 58                      default_bg=0x000000)
 59  
 60  # set backlight default to off
 61  backlight_off = 0
 62  backlight_on = 0.8
 63  pyportal.set_backlight(backlight_off)
 64  
 65  # assign fonts
 66  big_font = bitmap_font.load_font(cwd+"/fonts/Nunito-Light-75.bdf")
 67  big_font.load_glyphs(b'0123456789:AP') # pre-load glyphs for fast printing
 68  print('loading fonts...')
 69  info_font = bitmap_font.load_font(cwd+"/fonts/Nunito-Black-17.bdf")
 70  info_font.load_glyphs(b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.:/ ')
 71  
 72  time_color = 0xFFFFFF
 73  time_position = (75,130)
 74  time_textarea = Label(big_font, color=time_color,
 75                        x=time_position[0], y=time_position[1])
 76  
 77  wakeup_time_color = 0xFFFFFF
 78  wakeup_time_position = (15,200)
 79  wakeup_time_textarea = Label(info_font, color=wakeup_time_color,
 80                               x=wakeup_time_position[0], y=wakeup_time_position[1])
 81  
 82  light_on_time_color = 0xFFFFFF
 83  light_on_time_position = (15,220)
 84  light_on_time_textarea = Label(info_font, color=light_on_time_color,
 85                                 x=light_on_time_position[0], y=light_on_time_position[1])
 86  
 87  pyportal.splash.append(time_textarea)
 88  pyportal.splash.append(wakeup_time_textarea)
 89  pyportal.splash.append(light_on_time_textarea)
 90  
 91  while True:
 92      try:
 93          print("Getting time from internet!")
 94          pyportal.get_local_time()
 95      except RuntimeError as e:
 96          print("Some error occured, retrying! -", e)
 97          continue
 98      break
 99  
100  # parse given time string into hour minute and AM_PM elements
101  def parseTime(time_before):
102      hours_before, minutes_before = time_before.split(":")
103      AM_PM_str = minutes_before[-1:]
104      minutes_before = int(minutes_before[:-1])
105      if (hours_before != '12') and AM_PM_str == 'P':
106          hours_before = int(hours_before) + 12
107      elif ((hours_before == '12') and (AM_PM_str == 'P')):
108          hours_before = int(hours_before)
109      elif ((hours_before == '12') and (AM_PM_str == 'A')):
110          hours_before = 0
111      else:
112          hours_before = int(hours_before)
113      parsed_time = [hours_before, minutes_before]
114      return parsed_time
115  
116  # get time objects for wake up times
117  val_times = []
118  parsed_times = []
119  for i in range(len(wake_up_times)):
120      parsed_time_day = parseTime(wake_up_times[i])
121      hours, minutes = parsed_time_day[0:2]
122      now_day = time.localtime()
123      time_obj_mk = time.mktime((now_day[0], now_day[1], now_day[2], hours,
124                                 minutes, now_day[5], i, now_day[7], now_day[8]))
125      time_obj = time.localtime(time_obj_mk)
126      val_times.append(time_obj_mk)
127      parsed_times.append(time_obj)
128  
129  # determine which day it is and print which time waking up on screen
130  def whichDay():
131      now = time.localtime()
132      current_day = now[6]
133      now_mk = time.mktime((now[0], now[1], now[2], now[3], now[4], now[5], now[6], now[7], now[8]))
134      # if it's after midnight and before todays wakeup time, display the wake up time of today
135      for day in range(len(wake_up_times)):
136          if now_mk < val_times[day]:
137              if current_day == day:
138                  input_wake_up_time = wake_up_times[day]
139                  use_day = day
140      # set wake up time to the next day's wake up time after current day's wake up time
141          else:
142              if current_day == 6:
143                  input_wake_up_time = wake_up_times[0]
144                  use_day = 0
145              else:
146                  if current_day == day:
147                      input_wake_up_time = wake_up_times[day+1]
148                      use_day = day + 1
149      input_wake_up_time_text = "Wake up " + days_str[use_day] + " at " + input_wake_up_time
150      wakeup_time_textarea.text = input_wake_up_time_text
151      return use_day
152  
153  def displayTime():
154      now = time.localtime()
155      hour, minute = now[3:5]
156      print(now)
157      print("Current time: %02d:%02d" % (hour, minute))
158      formatTime(hour, minute)
159      time_textarea.text = formatTime(hour, minute)
160      return formatTime(hour, minute)
161  
162  def formatTime(raw_hours, raw_minutes):
163      # display the time in a nice big font
164      format_str = "%d:%02d"
165      if raw_hours >= 12:
166          raw_hours -= 12
167          format_str = format_str+"P"
168      else:
169          format_str = format_str+"A"
170      if raw_hours == 0:
171          raw_hours = 12
172      time_str = format_str % (raw_hours, raw_minutes)
173      return time_str
174  
175  def backLight():
176      now = time.localtime()
177      now_val = time.mktime((now[0], now[1], now[2], now[3], now[4], now[5], now[6], now[7], now[8]))
178      wake_up_day_val = val_times[now[6]]
179      # if time is more than 9 hours after current day's wake up time,
180      # or time is before light start time, backlight off, tap to turn on
181      if (now_val - wake_up_day_val) > 32400 or (now_val - wake_up_day_val) < -1800:
182          pyportal.set_backlight(backlight_off)
183          if pyportal.touchscreen.touch_point:
184              pyportal.set_backlight(backlight_on)
185              time.sleep(5)
186              pyportal.set_backlight(backlight_off)
187      else:
188          pyportal.set_backlight(backlight_on)
189  
190  def subtract30min(day): # subtract 30 min
191      # get the time object from the corresponding day
192      raw_wake_up_time = parsed_times[day]
193      now = time.localtime()
194      # new time subtracting 30 min from wake up time
195      minus30 = time.mktime((now[0], now[1], now[2], raw_wake_up_time[3],
196                             raw_wake_up_time[4] - 30, now[5], now[6], now[7], now[8]))
197      time_minus30 = time.localtime(minus30)
198      hour_minus30 = time_minus30[3]
199      minutes_minus30 = time_minus30[4]
200      light_on_time_textarea.text = "Light starting at: " + formatTime(hour_minus30, minutes_minus30)
201      return formatTime(hour_minus30, minutes_minus30)
202  
203  refresh_time = None
204  
205  while True:
206      time_now = time.localtime()
207      # only query the online time once per hour (and on first run)
208      if (not refresh_time) or (time.monotonic() - refresh_time) > 3600:
209          try:
210              print("Getting time from internet!")
211              pyportal.get_local_time()
212              refresh_time = time.monotonic()
213          except RuntimeError as e:
214              print("Some error occured, retrying! -", e)
215              continue
216      time_str_text = displayTime()
217      print(time_str_text)
218      # determine which wake up time to choose based on the day
219      wake_up_day = whichDay()
220      # if time is more than 9 hours after previous day's wake up time,
221      # backlight off and can tap to turn on
222      backLight()
223      # start the light 30 min before wake up time
224      start_light_time = subtract30min(wake_up_day)
225      # If current day is same as wake up day and
226      # wake up time - 30 minutes equals current time, start the light
227      if wake_up_day == time_now[6] and time_str_text == start_light_time:
228          print("Starting wake up light")
229          # turn on backlight
230          pyportal.set_backlight(backlight_on)
231          for i in range(light_minutes - 1):
232              BRIGHTNESS = BRIGHTNESS + (MAX_BRIGHTNESS/light_minutes) # max 0.25, min 0.0
233              strip.fill(WHITE)
234              strip.brightness = BRIGHTNESS
235              displayTime()
236              time.sleep(60) # 60 for once per min
237          while not pyportal.touchscreen.touch_point: # turn strip off
238              displayTime()
239              time.sleep(1)
240              continue
241          strip.brightness = MIN_BRIGHTNESS
242      # update every second so that screen can be tapped to view time
243      time.sleep(1)