Skip to content

Commit edd29bb

Browse files
authored
Merge pull request #566 from dastels/pyportal_alarm_clock
Add PyPortal alarm clock stuff
2 parents 7a7cdd5 + b5151a4 commit edd29bb

33 files changed

Lines changed: 84 additions & 45 deletions
-225 KB
Binary file not shown.
-225 KB
Binary file not shown.

PyPortal_AlarmClock/red_alert.bmp

-225 KB
Binary file not shown.
-225 KB
Binary file not shown.
Lines changed: 84 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@
1212
All text above must be included in any redistribution.
1313
"""
1414

15-
#pylint:disable=redefined-outer-name,no-member,global-statement,no-self-use
16-
#pylint:disable=too-many-branches,too-many-statements,useless-super-delegation
15+
#pylint:disable=redefined-outer-name,no-member,global-statement
16+
#pylint:disable=no-self-use,too-many-branches,too-many-statements
17+
#pylint:disable=useless-super-delegation, too-many-locals
1718

1819
import time
1920
import json
2021
from secrets import secrets
2122
import board
2223
from adafruit_pyportal import PyPortal
2324
from adafruit_bitmap_font import bitmap_font
24-
from adafruit_display_text.text_area import TextArea
25+
from adafruit_display_text.label import Label
2526
from digitalio import DigitalInOut, Direction, Pull
2627
import analogio
2728
import displayio
29+
import adafruit_logging as logging
2830

2931
# Set up where we'll be fetching data from
3032
DATA_SOURCE = 'http://api.openweathermap.org/data/2.5/weather?id='+secrets['city_id']
@@ -37,8 +39,7 @@
3739

3840
pyportal = PyPortal(url=DATA_SOURCE,
3941
json_path=DATA_LOCATION,
40-
status_neopixel=board.NEOPIXEL,
41-
splash_max=10)
42+
status_neopixel=board.NEOPIXEL)
4243

4344
light = analogio.AnalogIn(board.LIGHT)
4445

@@ -56,11 +57,14 @@
5657
alarm_enabled = True
5758
alarm_armed = True
5859
alarm_interval = 5.0
59-
alarm_hour = 23
60-
alarm_minute = 20
60+
alarm_hour = 9
61+
alarm_minute = 45
6162
snooze_time = None
6263
snooze_interval = 600.0
6364

65+
# mugsy support
66+
mugsy_background = 'mugsy_background.bmp'
67+
6468
# weather support
6569

6670
icon_file = None
@@ -81,15 +85,20 @@
8185
####################
8286
# Load the fonts
8387

84-
large_font = bitmap_font.load_font('/fonts/Anton-Regular-104.bdf')
85-
large_font.load_glyphs(b'0123456789:') # pre-load glyphs for fast printing
88+
time_font = bitmap_font.load_font('/fonts/Anton-Regular-104.bdf')
89+
time_font.load_glyphs(b'0123456789:') # pre-load glyphs for fast printing
90+
91+
alarm_font = bitmap_font.load_font('/fonts/Helvetica-Bold-36.bdf')
92+
alarm_font.load_glyphs(b'0123456789:')
8693

87-
medium_font = bitmap_font.load_font('/fonts/Helvetica-Bold-36.bdf')
88-
medium_font.load_glyphs(b'0123456789:')
94+
temperature_font = bitmap_font.load_font('/fonts/Arial-16.bdf')
95+
temperature_font.load_glyphs(b'0123456789CF')
8996

90-
small_font = bitmap_font.load_font('/fonts/Arial-16.bdf')
91-
small_font.load_glyphs(b'0123456789CF')
97+
####################
98+
# Set up logging
9299

100+
logger = logging.getLogger('alarm_clock')
101+
logger.setLevel(logging.ERROR) # change as desired
93102

94103
####################
95104
# Functions
@@ -98,7 +107,7 @@ def create_text_areas(configs):
98107
"""Given a list of area specifications, create and return test areas."""
99108
text_areas = []
100109
for cfg in configs:
101-
textarea = TextArea(cfg['font'], text=' '*cfg['size'])
110+
textarea = Label(cfg['font'], text=' '*cfg['size'])
102111
textarea.x = cfg['x']
103112
textarea.y = cfg['y']
104113
textarea.color = cfg['color']
@@ -112,7 +121,9 @@ def clear_splash():
112121

113122

114123
def touch_in_button(t, b):
115-
return (b['left'] >= t[0] >= b['right']) and (b['top'] <= t[1] <= b['bottom'])
124+
in_horizontal = b['left'] <= t[0] <= b['right']
125+
in_vertical = b['top'] <= t[1] <= b['bottom']
126+
return in_horizontal and in_vertical
116127

117128

118129
touched = False
@@ -165,9 +176,9 @@ def __init__(self):
165176
self.refresh_time = None
166177
self.update_time = None
167178
self.weather_refresh = None
168-
text_area_configs = [dict(x=88, y=30, size=5, color=0xFFFFFF, font=large_font),
169-
dict(x=210, y=10, size=5, color=0xFF0000, font=medium_font),
170-
dict(x=88, y=65, size=6, color=0xFFFFFF, font=small_font)]
179+
text_area_configs = [dict(x=88, y=170, size=5, color=0xFFFFFF, font=time_font),
180+
dict(x=210, y=50, size=5, color=0xFF0000, font=alarm_font),
181+
dict(x=88, y=90, size=6, color=0xFFFFFF, font=temperature_font)]
171182
self.text_areas = create_text_areas(text_area_configs)
172183
self.weather_icon = displayio.Group(max_size=1)
173184
self.weather_icon.x = 88
@@ -180,8 +191,8 @@ def __init__(self):
180191
self.snooze_file = None
181192

182193
# each button has it's edges as well as the state to transition to when touched
183-
self.buttons = [dict(left=320, top=50, right=240, bottom=120, next_state='settings'),
184-
dict(left=320, top=155, right=240, bottom=220, next_state='mugsy')]
194+
self.buttons = [dict(left=0, top=50, right=80, bottom=120, next_state='settings'),
195+
dict(left=0, top=155, right=80, bottom=220, next_state='mugsy')]
185196

186197

187198
@property
@@ -218,15 +229,16 @@ def tick(self, now):
218229
return
219230

220231
# check light level and adjust background & backlight
221-
self.adjust_backlight_based_on_light()
232+
#self.adjust_backlight_based_on_light()
222233

223234
# only query the online time once per hour (and on first run)
224235
if (not self.refresh_time) or ((now - self.refresh_time) > 3600):
225236
try:
226237
pyportal.get_local_time(location=secrets['timezone'])
227238
self.refresh_time = now
228239
except RuntimeError as e:
229-
print('Some error occured, retrying! -', e)
240+
self.refresh_time = now - 3000 # delay 10 minutes before retrying
241+
logger.error('Some error occured, retrying! - %s', str(e))
230242

231243
# only query the weather every 10 minutes (and on first run)
232244
if (not self.weather_refresh) or (now - self.weather_refresh) > 600:
@@ -246,9 +258,15 @@ def tick(self, now):
246258
self.icon_file.close()
247259
self.icon_file = open(filename, "rb")
248260
icon = displayio.OnDiskBitmap(self.icon_file)
249-
icon_sprite = displayio.TileGrid(icon,
250-
pixel_shader=displayio.ColorConverter(),
251-
position=(0, 0))
261+
try:
262+
icon_sprite = displayio.TileGrid(icon,
263+
pixel_shader=displayio.ColorConverter(),
264+
x=0, y=0)
265+
except TypeError:
266+
icon_sprite = displayio.TileGrid(icon,
267+
pixel_shader=displayio.ColorConverter(),
268+
position=(0, 0))
269+
252270

253271
self.weather_icon.append(icon_sprite)
254272

@@ -263,13 +281,15 @@ def tick(self, now):
263281
board.DISPLAY.wait_for_frame()
264282

265283
except RuntimeError as e:
266-
print("Some error occured, retrying! -", e)
284+
self.weather_refresh = now - 540 # delay a minute before retrying
285+
logger.error("Some error occured, retrying! - %s", str(e))
267286

268287
if (not update_time) or ((now - update_time) > 30):
269288
# Update the time
270289
update_time = now
271290
the_time = time.localtime()
272-
self.text_areas[0].text = '%02d:%02d' % (the_time.tm_hour,the_time.tm_min)
291+
time_string = '%02d:%02d' % (the_time.tm_hour,the_time.tm_min)
292+
self.text_areas[0].text = time_string
273293
board.DISPLAY.refresh_soon()
274294
board.DISPLAY.wait_for_frame()
275295

@@ -285,6 +305,8 @@ def tick(self, now):
285305

286306

287307
def touch(self, t, touched):
308+
if t:
309+
logger.debug('touched: %d, %d', t[0], t[1])
288310
if t and not touched: # only process the initial touch
289311
for button_index in range(len(self.buttons)):
290312
b = self.buttons[button_index]
@@ -304,9 +326,14 @@ def enter(self):
304326
self.snooze_file.close()
305327
self.snooze_file = open('/icons/zzz.bmp', "rb")
306328
icon = displayio.OnDiskBitmap(self.snooze_file)
307-
icon_sprite = displayio.TileGrid(icon,
308-
pixel_shader=displayio.ColorConverter(),
309-
position=(0, 0))
329+
try:
330+
icon_sprite = displayio.TileGrid(icon,
331+
pixel_shader=displayio.ColorConverter(),
332+
x=0, y=0)
333+
except TypeError:
334+
icon_sprite = displayio.TileGrid(icon,
335+
pixel_shader=displayio.ColorConverter(),
336+
position=(0, 0))
310337
self.snooze_icon.append(icon_sprite)
311338

312339
pyportal.splash.append(self.snooze_icon)
@@ -318,11 +345,6 @@ def enter(self):
318345
board.DISPLAY.wait_for_frame()
319346

320347

321-
def exit(self):
322-
super().exit()
323-
for _ in range(len(self.snooze_icon)):
324-
self.snooze_icon.pop()
325-
326348

327349
class Mugsy_State(Time_State):
328350
"""This state tells Mugsey 'Make me a coffee' """
@@ -340,6 +362,15 @@ def tick(self, now):
340362
# Once the job is done, go back to the main screen
341363
change_to_state('time')
342364

365+
def enter(self):
366+
global low_light
367+
low_light = False
368+
pyportal.set_backlight(1.00)
369+
pyportal.set_background(mugsy_background)
370+
board.DISPLAY.refresh_soon()
371+
board.DISPLAY.wait_for_frame()
372+
373+
343374

344375
class Alarm_State(State):
345376
"""This state shows/sounds the alarm.
@@ -403,14 +434,14 @@ def __init__(self):
403434
super().__init__()
404435
self.previous_touch = None
405436
self.background = 'settings_background.bmp'
406-
text_area_configs = [dict(x=88, y=-10, size=5, color=0xFFFFFF, font=large_font)]
437+
text_area_configs = [dict(x=88, y=120, size=5, color=0xFFFFFF, font=time_font)]
407438

408439
self.text_areas = create_text_areas(text_area_configs)
409-
self.buttons = [dict(left=320, top=30, right=240, bottom=93), # on
410-
dict(left=320, top=98, right=240, bottom=152), # return
411-
dict(left=320, top=155, right=240, bottom=220), # off
412-
dict(left=240, top=0, right=120, bottom = 240), # hours
413-
dict(left=120, top=0, right=0, bottom = 240)] # minutes
440+
self.buttons = [dict(left=0, top=30, right=80, bottom=93), # on
441+
dict(left=0, top=98, right=80, bottom=152), # return
442+
dict(left=0, top=155, right=80, bottom=220), # off
443+
dict(left=100, top=0, right=200, bottom = 240), # hours
444+
dict(left=220, top=0, right=320, bottom = 240)] # minutes
414445

415446

416447
@property
@@ -421,28 +452,34 @@ def name(self):
421452
def touch(self, t, touched):
422453
global alarm_hour, alarm_minute, alarm_enabled
423454
if t:
455+
logger.debug('touched: %d, %d', t[0], t[1])
424456
if touch_in_button(t, self.buttons[0]): # on
457+
logger.debug('ON touched')
425458
alarm_enabled = True
426459
self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)
427460
elif touch_in_button(t, self.buttons[1]): # return
461+
logger.debug('RETURN touched')
428462
change_to_state('time')
429463
elif touch_in_button(t, self.buttons[2]): # off
464+
logger.debug('OFF touched')
430465
alarm_enabled = False
431466
self.text_areas[0].text = ' '
432467
elif alarm_enabled:
433468
if not self.previous_touch:
434469
self.previous_touch = t
435470
else:
436471
if touch_in_button(t, self.buttons[3]): # HOURS
437-
if t[1] < (self.previous_touch[1]): # moving up
472+
logger.debug('HOURS touched')
473+
if t[1] < (self.previous_touch[1] - 5): # moving up
438474
alarm_hour = (alarm_hour + 1) % 24
439-
elif t[1] > (self.previous_touch[1]): # moving down
475+
elif t[1] > (self.previous_touch[1] + 5): # moving down
440476
alarm_hour = (alarm_hour - 1) % 24
441477
self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)
442478
elif touch_in_button(t, self.buttons[4]): # MINUTES
443-
if t[1] < (self.previous_touch[1]): # moving up
479+
logger.debug('MINUTES touched')
480+
if t[1] < (self.previous_touch[1] - 5): # moving up
444481
alarm_minute = (alarm_minute + 1) % 60
445-
elif t[1] > (self.previous_touch[1]): # moving down
482+
elif t[1] > (self.previous_touch[1] + 5): # moving down
446483
alarm_minute = (alarm_minute - 1) % 60
447484
self.text_areas[0].text = '%02d:%02d' % (alarm_hour, alarm_minute)
448485
self.previous_touch = t
@@ -477,8 +514,10 @@ def enter(self):
477514
def change_to_state(state_name):
478515
global current_state
479516
if current_state:
517+
logger.debug('Exiting %s', current_state.name)
480518
current_state.exit()
481519
current_state = states[state_name]
520+
logger.debug('Entering %s', current_state.name)
482521
current_state.enter()
483522

484523
####################
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)