Skip to content

Commit 6696219

Browse files
authored
Initial upload of python files
Initial upload, doesn't include fonts or some backgrounds.
1 parent b00384f commit 6696219

11 files changed

+494
-0
lines changed

Diff for: code.py

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Michael McGurrin
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
"""
24+
This is the main program for a multi-function Portal app.
25+
It includes current weather, day, date, time, S&P 500, Indoor Temp,
26+
and Shower Thoughts subreddit.
27+
"""
28+
import sys
29+
import time
30+
import board
31+
from adafruit_pyportal import PyPortal
32+
from adafruit_bitmap_font import bitmap_font
33+
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
34+
sys.path.append(cwd)
35+
import day_graphics # pylint: disable=wrong-import-position
36+
import openweather_graphics # pylint: disable=wrong-import-position
37+
import st_graphics # pylint: disable=wrong-import-position
38+
import market_graphics
39+
40+
linger = 7 # number of seconds to stay on each app
41+
long_linger = 14 # For apps with more text, like shower thoughts
42+
43+
# Get wifi details and more from a secrets.py file
44+
try:
45+
from secrets import secrets
46+
except ImportError:
47+
print("WiFi secrets are kept in secrets.py, please add them there!")
48+
raise
49+
50+
# Initiate the pyportal
51+
DATA_LOCATION = []
52+
pyportal = PyPortal(json_path=DATA_LOCATION,
53+
status_neopixel=board.NEOPIXEL,
54+
default_bg=0x000000)
55+
56+
# Get the fonts for all apps
57+
small_font = cwd+"/fonts/Arial-12.bdf"
58+
medium_font = cwd+"/fonts/Arial-16.bdf"
59+
large_font = cwd+"/fonts/Arial-Bold-24.bdf"
60+
small_font = bitmap_font.load_font(small_font)
61+
medium_font = bitmap_font.load_font(medium_font)
62+
large_font = bitmap_font.load_font(large_font)
63+
glyphs = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: '
64+
small_font.load_glyphs(glyphs)
65+
medium_font.load_glyphs(glyphs)
66+
large_font.load_glyphs(glyphs)
67+
large_font.load_glyphs(('°',)) # a non-ascii character we need for sure
68+
69+
# Weather info
70+
# Use cityname, country code where countrycode is ISO3166 format.
71+
# E.g. "New York, US" or "London, GB" OR use Location Code
72+
LOCATION = "4791160" #Vienna, VA
73+
# Set up where we'll be fetching data from
74+
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
75+
WX_DATA_SOURCE = "https://api.openweathermap.org/data/2.5/weather?id="+LOCATION
76+
WX_DATA_SOURCE += "&appid="+secrets['openweather_token']
77+
78+
# S&P 500 ("Market Data") info
79+
MARKET_DATA_SOURCE = "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=INX"
80+
MARKET_DATA_SOURCE += "&apikey="+secrets['alpha_advantage_key']
81+
82+
# Shower thoughts info
83+
ST_DATA_SOURCE = "https://www.reddit.com/r/Showerthoughts/new.json?sort=new&limit=1"
84+
85+
# Loop through all the applications
86+
localtime_refresh = None
87+
weather_refresh = None
88+
st_refresh = None
89+
market_refresh = None
90+
while True:
91+
# Day, date, and time
92+
# only query the online time once per hour (and on first run)
93+
pyportal.set_background(cwd+"/day_bitmap.bmp")
94+
try:
95+
if (not localtime_refresh) or (time.monotonic() - localtime_refresh) > 3600:
96+
print("Getting time from internet!")
97+
pyportal.get_local_time()
98+
localtime_refresh = time.monotonic()
99+
text_group = day_graphics.day_graphics(medium_font = medium_font, large_font = large_font)
100+
pyportal.splash.append(text_group)
101+
# Display for seven seconds, then empty the pyportal.splash group so it can be loaded with new display info
102+
time.sleep(linger)
103+
except RuntimeError as e:
104+
print("Some error occured, skipping this iteration! -", e)
105+
continue
106+
pyportal.splash.pop()
107+
108+
# While there are some differences, could probably refactor the three segments
109+
# below into one function call
110+
111+
# Display weather
112+
# only query the weather every 10 minutes (and on first run)
113+
pyportal.set_background(cwd+"/wx_bitmap.bmp")
114+
time.sleep(2)
115+
try:
116+
if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600:
117+
wx = pyportal.fetch(WX_DATA_SOURCE)
118+
print("Response is", wx)
119+
weather_refresh = time.monotonic()
120+
text_group, background_file = openweather_graphics.wx_graphics(medium_font = medium_font,
121+
large_font = large_font, small_font = small_font, weather = wx)
122+
pyportal.set_background(cwd+background_file)
123+
pyportal.splash.append(text_group)
124+
# Display for 14 seconds, then empty the pyportal.splash group so it can be loaded with new display info
125+
time.sleep(long_linger)
126+
except RuntimeError as e:
127+
print("Some error occured, skipping this iteration! -", e)
128+
continue
129+
pyportal.splash.pop()
130+
131+
# Display Reddit Shower Thoughts
132+
# only query shower thoughts every 5 minutes (and on first run)
133+
pyportal.set_background(cwd+"/st_bitmap.bmp")
134+
time.sleep(2)
135+
try:
136+
if (not st_refresh) or (time.monotonic() - st_refresh) > 300:
137+
print("Getting shower thought from internet!")
138+
st = pyportal.fetch(ST_DATA_SOURCE)
139+
print("Response is", st)
140+
st_refresh = time.monotonic()
141+
text_group = st_graphics.st_graphics(medium_font = medium_font, large_font = large_font,
142+
small_font = small_font, st = st)
143+
pyportal.splash.append(text_group)
144+
# Display for 14 seconds, then empty the pyportal.splash group so it can be loaded with new display info
145+
time.sleep(long_linger)
146+
except RuntimeError as e:
147+
print("Some error occured, skipping this iteration! -", e)
148+
continue
149+
pyportal.splash.pop()
150+
151+
# Display S&P 500
152+
# only query the S&P every 10 minutes (and on first run)
153+
pyportal.set_background(cwd+"/market_bitmap.bmp")
154+
time.sleep(2)
155+
try:
156+
if (not market_refresh) or (time.monotonic() - market_refresh) > 300:
157+
print("Getting S&P 500 from internet!")
158+
market = pyportal.fetch(MARKET_DATA_SOURCE)
159+
print("Response is", market)
160+
market_refresh = time.monotonic()
161+
text_group, background_file = market_graphics.market_graphics(medium_font = medium_font, large_font = large_font,
162+
market = market)
163+
pyportal.set_background(cwd+background_file)
164+
pyportal.splash.append(text_group)
165+
# Display for 7 seconds, then empty the pyportal.splash group so it can be loaded with new display info
166+
time.sleep(linger)
167+
except RuntimeError as e:
168+
print("Some error occured, skipping this iteration! -", e)
169+
continue
170+
pyportal.splash.pop()

Diff for: day_bitmap.bmp

76.1 KB
Binary file not shown.

Diff for: day_graphics.py

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Michael McGurrin
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
import time
24+
#import json
25+
import displayio
26+
from adafruit_display_text.label import Label
27+
from adafruit_bitmap_font import bitmap_font
28+
29+
# cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
30+
31+
def day_graphics(medium_font, large_font):
32+
"""Format the day, date, and time info from time.localtime() for display.
33+
Inputs are medium and large fonts.
34+
"""
35+
text_group = displayio.Group(max_size = 5)
36+
37+
ur_label = Label(medium_font, max_glyphs=8) # time text
38+
ur_label.x = 220
39+
ur_label.y = 25
40+
ur_label.color = 0xFFFFFF
41+
text_group.append(ur_label)
42+
43+
sub_label = Label(medium_font, max_glyphs=18) # month, date, year text
44+
sub_label.x = 60
45+
sub_label.y = 140
46+
sub_label.color = 0xFFFFFF
47+
text_group.append(sub_label)
48+
49+
main_label = Label(large_font, max_glyphs=9) # day of week
50+
main_label.x = 80
51+
main_label.y = 100
52+
main_label.color = 0xFFFFFF
53+
text_group.append(main_label)
54+
55+
"""Fetch the time.localtime(), parse it out and update the display text"""
56+
day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
57+
month_names = ["Unknown", "January", "Febuary", "March", "April", "May", "June", "July",
58+
"August", "September", "October", "November", "December"]
59+
now = time.localtime()
60+
day_str = day_names[now[6]]
61+
date_str = str(now[2])
62+
month_str = month_names[now[1]]
63+
year_str = str(now[0])
64+
day_date_str = month_str + " " + date_str + ", " + year_str
65+
sub_label.text = day_date_str
66+
main_label.text = day_str
67+
hour = now[3]
68+
minute = now[4]
69+
format_str = "%d:%02d"
70+
if hour >= 12:
71+
hour -= 12
72+
format_str = format_str+" PM"
73+
else:
74+
format_str = format_str+" AM"
75+
if hour == 0:
76+
hour = 12
77+
time_str = format_str % (hour, minute)
78+
ur_label.text = time_str
79+
return(text_group)

Diff for: market_bitmap.bmp

76.1 KB
Binary file not shown.

Diff for: market_graphics.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# The MIT License (MIT)
2+
#
3+
# Copyright (c) 2019 Michael McGurrin
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
23+
import time
24+
import json
25+
import displayio
26+
from adafruit_display_text.label import Label
27+
from adafruit_bitmap_font import bitmap_font
28+
29+
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
30+
def market_graphics(medium_font, large_font, market):
31+
"""Take input JSON pulled from the Alpha Vantage API, extract the relevent data,
32+
and format for display. Inputs are medium and large fonts and the JSON input from
33+
the API
34+
"""
35+
text_group = displayio.Group(max_size = 2)
36+
market = json.loads(market)
37+
38+
main_text = Label(large_font, max_glyphs=20)
39+
main_text.x = 40
40+
main_text.y = 80
41+
main_text.color = 0xFFFFFF
42+
text_group.append(main_text)
43+
44+
sub_text = Label(medium_font, max_glyphs=20)
45+
sub_text.x = 30
46+
sub_text.y = 120
47+
text_group.append(sub_text)
48+
49+
price = market['Global Quote']['05. price']
50+
price = price[:-2] # only want 2 decimal points
51+
change = market['Global Quote']['09. change']
52+
change = change[:-2] # only want two decimal points
53+
percent = market['Global Quote']['10. change percent']
54+
l = len(percent)-3
55+
percent = percent[:l] + percent[-1] # only want two decimal points
56+
print(price, change, percent)
57+
58+
main_text.text = price
59+
sub_text.text = change + ' (' + percent + ')'
60+
61+
# set the background and color of change
62+
if float(change) >= 0:
63+
background_file = "/icons/green_up.bmp"
64+
sub_text.color = 0x00FF00
65+
else:
66+
background_file = "/icons/red_down.bmp"
67+
sub_text.color = 0xFF0000
68+
return (text_group, background_file)
69+
70+

0 commit comments

Comments
 (0)