Skip to content

Commit 191ee82

Browse files
authored
Merge pull request adafruit#1004 from adafruit/pyPortalTitanoWeather
adding titano weather station code & files
2 parents 11fbffc + 7f3601c commit 191ee82

34 files changed

+13231
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
alarms = {
2+
'bed' : '10:00 PM',
3+
'breakfast' : '8:00 AM',
4+
'lunch' : '12:00 PM',
5+
'dinner' : '6:30 PM',
6+
'trash' : ('Fri.', '1:00 PM')
7+
}
8+
9+
timers = {
10+
'snooze_time' : 300
11+
}
12+
13+
holidays = {
14+
'new years' : ('Jan. 1', 'Happy New Year!'),
15+
'valentines' : ('Feb. 14', "Happy Valentine's Day! <3"),
16+
'halloween' : ('Oct. 31', 'Happy Halloween!'),
17+
'xmas' : ('Dec. 25', 'Merry Christmas!')
18+
}
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
import time
2+
from calendar import alarms
3+
from calendar import timers
4+
import board
5+
import displayio
6+
from digitalio import DigitalInOut, Direction, Pull
7+
from adafruit_button import Button
8+
from adafruit_pyportal import PyPortal
9+
import openweather_graphics # pylint: disable=wrong-import-position
10+
11+
# Get wifi details and more from a secrets.py file
12+
try:
13+
from secrets import secrets
14+
except ImportError:
15+
print("WiFi secrets are kept in secrets.py, please add them there!")
16+
raise
17+
18+
# Use cityname, country code where countrycode is ISO3166 format.
19+
# E.g. "New York, US" or "London, GB"
20+
LOCATION = secrets['location']
21+
22+
# Set up where we'll be fetching data from
23+
DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q="+LOCATION
24+
DATA_SOURCE += "&appid="+secrets['openweather_token']
25+
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
26+
DATA_LOCATION = []
27+
28+
# Initialize the pyportal object and let us know what data to fetch and where
29+
# to display it
30+
pyportal = PyPortal(url=DATA_SOURCE,
31+
json_path=DATA_LOCATION,
32+
status_neopixel=board.NEOPIXEL,
33+
default_bg=0x000000)
34+
35+
display = board.DISPLAY
36+
37+
# the alarm sound file locations
38+
alarm_sound_trash = "/sounds/trash.wav"
39+
alarm_sound_bed = "/sounds/sleep.wav"
40+
alarm_sound_eat = "/sounds/eat.wav"
41+
42+
# the alarm sounds in an array that matches the order of the gfx & alarm check-ins
43+
alarm_sounds = [alarm_sound_trash, alarm_sound_bed,
44+
alarm_sound_eat, alarm_sound_eat, alarm_sound_eat]
45+
46+
# setting up the bitmaps for the alarms
47+
48+
# sleep alarm
49+
sleep_bitmap = displayio.OnDiskBitmap(open("/sleepBMP.bmp", "rb"))
50+
sleep_tilegrid = displayio.TileGrid(sleep_bitmap, pixel_shader=displayio.ColorConverter())
51+
group_bed = displayio.Group()
52+
group_bed.append(sleep_tilegrid)
53+
54+
# trash alarm
55+
trash_bitmap = displayio.OnDiskBitmap(open("/trashBMP.bmp", "rb"))
56+
trash_tilegrid = displayio.TileGrid(trash_bitmap, pixel_shader=displayio.ColorConverter())
57+
group_trash = displayio.Group()
58+
group_trash.append(trash_tilegrid)
59+
60+
# meal alarm
61+
eat_bitmap = displayio.OnDiskBitmap(open("/eatBMP.bmp", "rb"))
62+
eat_tilegrid = displayio.TileGrid(eat_bitmap, pixel_shader=displayio.ColorConverter())
63+
group_eat = displayio.Group()
64+
group_eat.append(eat_tilegrid)
65+
66+
# snooze touch screen buttons
67+
# one for each alarm bitmap
68+
snooze_controls = [
69+
{'label': "snooze_trash", 'pos': (4, 222), 'size': (236, 90), 'color': None},
70+
{'label': "snooze_bed", 'pos': (4, 222), 'size': (236, 90), 'color': None},
71+
{'label': "snooze_eat", 'pos': (4, 222), 'size': (236, 90), 'color': None},
72+
]
73+
74+
# setting up the snooze buttons as buttons
75+
snooze_buttons = []
76+
for s in snooze_controls:
77+
snooze_button = Button(x=s['pos'][0], y=s['pos'][1],
78+
width=s['size'][0], height=s['size'][1],
79+
style=Button.RECT,
80+
fill_color=s['color'], outline_color=None,
81+
name=s['label'])
82+
snooze_buttons.append(snooze_button)
83+
84+
# dismiss touch screen buttons
85+
# one for each alarm bitmap
86+
dismiss_controls = [
87+
{'label': "dismiss_trash", 'pos': (245, 222), 'size': (230, 90), 'color': None},
88+
{'label': "dismiss_bed", 'pos': (245, 222), 'size': (230, 90), 'color': None},
89+
{'label': "dismiss_eat", 'pos': (245, 222), 'size': (230, 90), 'color': None},
90+
]
91+
92+
# setting up the dismiss buttons as buttons
93+
dismiss_buttons = []
94+
for d in dismiss_controls:
95+
dismiss_button = Button(x=d['pos'][0], y=d['pos'][1],
96+
width=d['size'][0], height=d['size'][1],
97+
style=Button.RECT,
98+
fill_color=d['color'], outline_color=None,
99+
name=d['label'])
100+
dismiss_buttons.append(dismiss_button)
101+
102+
# adding the touch screen buttons to the different alarm gfx groups
103+
group_trash.append(snooze_buttons[0].group)
104+
group_trash.append(dismiss_buttons[0].group)
105+
group_bed.append(snooze_buttons[1].group)
106+
group_bed.append(dismiss_buttons[1].group)
107+
group_eat.append(snooze_buttons[2].group)
108+
group_eat.append(dismiss_buttons[2].group)
109+
110+
# setting up the hardware snooze/dismiss buttons
111+
switch_snooze = DigitalInOut(board.D3)
112+
switch_snooze.direction = Direction.INPUT
113+
switch_snooze.pull = Pull.UP
114+
115+
switch_dismiss = DigitalInOut(board.D4)
116+
switch_dismiss.direction = Direction.INPUT
117+
switch_dismiss.pull = Pull.UP
118+
119+
# grabbing the alarm times from the calendar file
120+
# 'None' is the placeholder for trash, which is weekly rather than daily
121+
alarm_checks = [None, alarms['bed'],alarms['breakfast'],alarms['lunch'],alarms['dinner']]
122+
# all of the alarm graphics
123+
alarm_gfx = [group_trash, group_bed, group_eat, group_eat, group_eat]
124+
125+
# allows for the openweather_graphics to show
126+
gfx = openweather_graphics.OpenWeather_Graphics(pyportal.splash, am_pm=True, celsius=False)
127+
128+
# state machines
129+
localtile_refresh = None
130+
weather_refresh = None
131+
dismissed = None
132+
touched = None
133+
start = None
134+
alarm = None
135+
snoozed = None
136+
touch_button_snooze = None
137+
touch_button_dismiss = None
138+
phys_dismiss = None
139+
phys_snooze = None
140+
mode = 0
141+
button_mode = 0
142+
143+
# weekday array
144+
weekday = ["Mon.", "Tues.", "Wed.", "Thurs.", "Fri.", "Sat.", "Sun."]
145+
146+
# weekly alarm setup. checks for weekday and time
147+
weekly_alarms = [alarms['trash']]
148+
weekly_day = [alarms['trash'][0]]
149+
weekly_time = [alarms['trash'][1]]
150+
151+
while True:
152+
# while esp.is_connected:
153+
# only query the online time once per hour (and on first run)
154+
if (not localtile_refresh) or (time.monotonic() - localtile_refresh) > 3600:
155+
try:
156+
print("Getting time from internet!")
157+
pyportal.get_local_time()
158+
localtile_refresh = time.monotonic()
159+
except RuntimeError as e:
160+
print("Some error occured, retrying! -", e)
161+
continue
162+
163+
if not alarm:
164+
# only query the weather every 10 minutes (and on first run)
165+
# only updates if an alarm is not active
166+
if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600:
167+
try:
168+
value = pyportal.fetch()
169+
print("Response is", value)
170+
gfx.display_weather(value)
171+
weather_refresh = time.monotonic()
172+
except RuntimeError as e:
173+
print("Some error occured, retrying! -", e)
174+
continue
175+
# updates time to check alarms
176+
# checks every 30 seconds
177+
# identical to def(update_time) in openweather_graphics.py
178+
if (not start) or (time.monotonic() - start) > 30:
179+
# grabs all the time data
180+
clock = time.localtime()
181+
date = clock[2]
182+
hour = clock[3]
183+
minute = clock[4]
184+
day = clock[6]
185+
today = weekday[day]
186+
format_str = "%d:%02d"
187+
date_format_str = " %d, %d"
188+
if hour >= 12:
189+
hour -= 12
190+
format_str = format_str+" PM"
191+
else:
192+
format_str = format_str+" AM"
193+
if hour == 0:
194+
hour = 12
195+
# formats date display
196+
today_str = today
197+
time_str = format_str % (hour, minute)
198+
# checks for weekly alarms
199+
for i in weekly_alarms:
200+
w = weekly_alarms.index(i)
201+
if time_str == weekly_time[w] and today == weekly_day[w]:
202+
print("trash time")
203+
alarm = True
204+
if alarm and not dismissed and not snoozed:
205+
display.show(alarm_gfx[w])
206+
pyportal.play_file(alarm_sounds[w])
207+
mode = w
208+
print("mode is:", mode)
209+
# checks for daily alarms
210+
for i in alarm_checks:
211+
a = alarm_checks.index(i)
212+
if time_str == alarm_checks[a]:
213+
alarm = True
214+
if alarm and not dismissed and not snoozed:
215+
display.show(alarm_gfx[a])
216+
pyportal.play_file(alarm_sounds[a])
217+
mode = a
218+
print(mode)
219+
# calls update_time() from openweather_graphics to update
220+
# clock display
221+
gfx.update_time()
222+
gfx.update_date()
223+
# resets time counter
224+
start = time.monotonic()
225+
226+
# allows for the touchscreen buttons to work
227+
if mode > 1:
228+
button_mode = 2
229+
else:
230+
button_mode = mode
231+
# print("button mode is", button_mode)
232+
233+
# hardware snooze/dismiss button setup
234+
if switch_dismiss.value and phys_dismiss:
235+
phys_dismiss = False
236+
if switch_snooze.value and phys_snooze:
237+
phys_snooze = False
238+
if not switch_dismiss.value and not phys_dismiss:
239+
phys_dismiss = True
240+
print("pressed dismiss button")
241+
dismissed = True
242+
alarm = False
243+
display.show(pyportal.splash)
244+
touched = time.monotonic()
245+
mode = mode
246+
if not switch_snooze.value and not phys_snooze:
247+
phys_snooze = True
248+
print("pressed snooze button")
249+
display.show(pyportal.splash)
250+
snoozed = True
251+
alarm = False
252+
touched = time.monotonic()
253+
mode = mode
254+
255+
# touchscreen button setup
256+
touch = pyportal.touchscreen.touch_point
257+
if not touch and touch_button_snooze:
258+
touch_button_snooze = False
259+
if not touch and touch_button_dismiss:
260+
touch_button_dismiss = False
261+
if touch:
262+
if snooze_buttons[button_mode].contains(touch) and not touch_button_snooze:
263+
print("Touched snooze")
264+
display.show(pyportal.splash)
265+
touch_button_snooze = True
266+
snoozed = True
267+
alarm = False
268+
touched = time.monotonic()
269+
mode = mode
270+
if dismiss_buttons[button_mode].contains(touch) and not touch_button_dismiss:
271+
print("Touched dismiss")
272+
dismissed = True
273+
alarm = False
274+
display.show(pyportal.splash)
275+
touch_button_dismiss = True
276+
touched = time.monotonic()
277+
mode = mode
278+
279+
# this is a little delay so that the dismissed state
280+
# doesn't collide with the alarm if it's dismissed
281+
# during the same time that the alarm activates
282+
if (not touched) or (time.monotonic() - touched) > 70:
283+
dismissed = False
284+
# snooze portion
285+
# pulls snooze_time from calendar and then when it's up
286+
# splashes the snoozed alarm's graphic, plays the alarm sound and goes back into
287+
# alarm state
288+
if (snoozed) and (time.monotonic() - touched) > timers['snooze_time']:
289+
print("snooze over")
290+
snoozed = False
291+
alarm = True
292+
mode = mode
293+
display.show(alarm_gfx[mode])
294+
pyportal.play_file(alarm_sounds[mode])
295+
print(mode)
450 KB
Binary file not shown.

0 commit comments

Comments
 (0)