Skip to content

Commit a1929d9

Browse files
authored
Merge pull request adafruit#1163 from fede2cr/master
Adding PyPortal Electioncal guide code
2 parents 9300a27 + 6e94323 commit a1929d9

File tree

7 files changed

+13640
-0
lines changed

7 files changed

+13640
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import sys
2+
import time
3+
import board
4+
from adafruit_pyportal import PyPortal
5+
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
6+
sys.path.append(cwd)
7+
import electioncal_graphics # pylint: disable=wrong-import-position
8+
9+
# Get wifi details and more from a secrets.py file
10+
try:
11+
from secrets import secrets
12+
except ImportError:
13+
print("WiFi secrets are kept in secrets.py, please add them there!")
14+
raise
15+
16+
# Change this to your state and county, replacing spaces for underscores and in lowercase
17+
STATE="new_york"
18+
COUNTY="new_york"
19+
20+
DATA_SOURCE = "https://electioncal.us/en/" + STATE +"/" + COUNTY + "/voter.json"
21+
DATA_LOCATION = []
22+
23+
# Initialize the pyportal object and let us know what data to fetch and where
24+
# to display it
25+
pyportal = PyPortal(url=DATA_SOURCE,
26+
json_path=DATA_LOCATION,
27+
status_neopixel=board.NEOPIXEL,
28+
default_bg=0x000000)
29+
30+
31+
gfx = electioncal_graphics.Electioncal_Graphics(pyportal.splash, am_pm=True)
32+
display_refresh = None
33+
while True:
34+
# only query the online time once per hour (and on first run)
35+
if (not display_refresh) or (time.monotonic() - display_refresh) > 3600:
36+
try:
37+
print("Getting time from internet!")
38+
pyportal.get_local_time()
39+
display_refresh = time.monotonic()
40+
except RuntimeError as e:
41+
print("Some error occured, retrying! -", e)
42+
continue
43+
44+
try:
45+
value = pyportal.fetch()
46+
#print("Response is", value)
47+
gfx.load_data(value)
48+
except RuntimeError as e:
49+
print("Some error occured, retrying! -", e)
50+
continue
51+
try:
52+
gfx.elections_cycle()
53+
except RuntimeError as e:
54+
print("Some error ocurred, retrying! -", e)
55+
continue
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import time
2+
import json
3+
import displayio
4+
from adafruit_display_text.label import Label
5+
from adafruit_bitmap_font import bitmap_font
6+
7+
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
8+
9+
small_font = cwd+"/fonts/Arial-12.bdf"
10+
medium_font = cwd+"/fonts/Arial-16.bdf"
11+
12+
class Electioncal_Graphics(displayio.Group):
13+
def __init__(self, root_group, *, am_pm=True):
14+
super().__init__(max_size=2)
15+
self.am_pm = am_pm
16+
root_group.append(self)
17+
self._icon_group = displayio.Group(max_size=1)
18+
self.append(self._icon_group)
19+
self._text_group = displayio.Group(max_size=9)
20+
self.append(self._text_group)
21+
22+
self._icon_sprite = None
23+
self._icon_file = None
24+
self.set_icon(cwd+"/icons/electioncal.bmp")
25+
26+
self.small_font = bitmap_font.load_font(small_font)
27+
self.medium_font = bitmap_font.load_font(medium_font)
28+
glyphs = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: '
29+
self.small_font.load_glyphs(glyphs)
30+
self.medium_font.load_glyphs(glyphs)
31+
32+
self.date_text = Label(self.small_font, max_glyphs=21)
33+
self.date_text.x = 15
34+
self.date_text.y = 195
35+
self.date_text.color = 0xFFFFFF
36+
self._text_group.append(self.date_text)
37+
38+
self.url_text = Label(self.small_font, max_glyphs=35)
39+
self.url_text.x = 15
40+
self.url_text.y = 220
41+
self.url_text.color = 0xFFFFFF
42+
self._text_group.append(self.url_text)
43+
self.url_text.text = "Visit us at https://electioncal.us"
44+
45+
self.state_text = Label(self.small_font, max_glyphs=60)
46+
self.state_text.x = 15
47+
self.state_text.y = 10
48+
self.state_text.color = 0xFFFFFF
49+
self._text_group.append(self.state_text)
50+
51+
self.election_date_text = Label(self.medium_font, max_glyphs=11)
52+
self.election_date_text.x = 15
53+
self.election_date_text.y = 60
54+
self.election_date_text.color = 0xFFFFFF
55+
self._text_group.append(self.election_date_text)
56+
57+
self.election_name_text = Label(self.small_font, max_glyphs=60)
58+
self.election_name_text.x = 15
59+
self.election_name_text.y = 95
60+
self.election_name_text.color = 0xFFFFFF
61+
self._text_group.append(self.election_name_text)
62+
63+
self.election_name_text_line2 = Label(self.small_font, max_glyphs=60)
64+
self.election_name_text_line2.x = 15
65+
self.election_name_text_line2.y = 120
66+
self.election_name_text_line2.color = 0xFFFFFF
67+
self._text_group.append(self.election_name_text_line2)
68+
69+
70+
def load_data(self, election_data):
71+
try:
72+
self.electioncal = json.loads(election_data)
73+
self.state_text.text = self.electioncal["dates"][1]["county"] + ", " + self.electioncal["dates"][0]["state"]
74+
except ValueError:
75+
print("Error loading JSON data: Please check the configuration of county and state, in code.py")
76+
raise
77+
78+
def elections_cycle(self):
79+
self.update_time()
80+
num_elections = len(self.electioncal["dates"])
81+
82+
for i in range(0,num_elections):
83+
if self.date_text.text[10:] < self.electioncal["dates"][i]["date"]:
84+
self.election_date_text.text = self.electioncal["dates"][i]["date"]
85+
# splitting the line at around 40 chars seems ok for regular PyPortal
86+
self.election_name_text_line2.text, self.election_name_text.text = self.paragrapher(self.electioncal["dates"][i]["name"], 40)
87+
time.sleep(30)
88+
89+
def update_time(self):
90+
"""Fetch the time.localtime(), parse it out and update the display text"""
91+
now = time.localtime()
92+
hour = now[3]
93+
minute = now[4]
94+
year = now[0]
95+
month = now[1]
96+
day = now[2]
97+
time_format_str = "%d:%02d"
98+
date_format_str = "%d-%02d-%02d"
99+
if self.am_pm:
100+
if hour >= 12:
101+
hour -= 12
102+
time_format_str = time_format_str+" PM"
103+
else:
104+
time_format_str = time_format_str+" AM"
105+
if hour == 0:
106+
hour = 12
107+
time_str = time_format_str % (hour, minute)
108+
date_str = date_format_str % (year, month, day)
109+
self.date_text.text = "Today is: " + date_str
110+
111+
def paragrapher(self, text, cut):
112+
""" Cuts a long line into two, having spaces in mind.
113+
Note we return line2 first as it looks better to clear the line2
114+
before printing a line1 with empty line2
115+
We run from cut, backwards till we find a space.
116+
"""
117+
if len(text) > cut:
118+
for i in range(cut,0,-1):
119+
if text[i] == " ":
120+
break
121+
line1 = text[0:i]
122+
line2 = text[i+1:80]
123+
else:
124+
line1 = text
125+
line2 = ""
126+
return line2, line1
127+
128+
def set_icon(self, filename):
129+
"""The background image to a bitmap file.
130+
131+
:param filename: The filename of the chosen icon
132+
133+
"""
134+
print("Set icon to ", filename)
135+
if self._icon_group:
136+
self._icon_group.pop()
137+
138+
if not filename:
139+
return # we're done, no icon desired
140+
if self._icon_file:
141+
self._icon_file.close()
142+
self._icon_file = open(filename, "rb")
143+
icon = displayio.OnDiskBitmap(self._icon_file)
144+
try:
145+
self._icon_sprite = displayio.TileGrid(icon,
146+
pixel_shader=displayio.ColorConverter())
147+
except TypeError:
148+
self._icon_sprite = displayio.TileGrid(icon,
149+
pixel_shader=displayio.ColorConverter(),
150+
position=(0,0))
151+
self._icon_group.append(self._icon_sprite)

0 commit comments

Comments
 (0)