Skip to content

Commit cf5a141

Browse files
committed
Added OSHWA MagTag project display
1 parent fe66035 commit cf5a141

4 files changed

Lines changed: 8922 additions & 0 deletions

File tree

5.13 KB
Binary file not shown.

oshwa_magtag_display/code.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import random
2+
import ssl
3+
import gc
4+
import wifi
5+
import socketpool
6+
import terminalio
7+
import adafruit_requests as requests
8+
from adafruit_magtag.magtag import MagTag
9+
10+
# Get wifi details and more from a secrets.py file
11+
try:
12+
from secrets import secrets
13+
except ImportError:
14+
print("WiFi secrets are kept in secrets.py, please add them there!")
15+
raise
16+
17+
# Initialize magtag object
18+
magtag = MagTag()
19+
20+
magtag.set_background("bmps/oshwa_full.bmp")
21+
22+
# Set up WiFi
23+
wifi.radio.connect(secrets["ssid"], secrets["password"])
24+
print(f"Connected to {secrets['ssid']}!")
25+
print("My IP address is", wifi.radio.ipv4_address)
26+
27+
socket = socketpool.SocketPool(wifi.radio)
28+
https = requests.Session(socket, ssl.create_default_context())
29+
30+
# Paste your API token below
31+
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwYTdlYmZiZjk2N2U2MDAxNzNmNWY0MCIsImlhdCI6MTYzMzM2NzM5NiwiZXhwIjoxNjQyMDA3Mzk2fQ.vJsMVR_b8QMl7APrqoEFNPXIq-jeLKXB8yUeQPIDK_o" # pylint: disable=line-too-long
32+
33+
def font_width_to_dict(font):
34+
# Reads the font file to determine how wide each character is
35+
# Used to avoid bad wrapping breaking the QR code
36+
chars = {}
37+
with open(font, "r") as file:
38+
for line in file:
39+
if "FONTBOUNDINGBOX" in line:
40+
size = int(line.split(" ")[1])
41+
if "ENCODING" in line and "_ENCODING" not in line:
42+
character = chr(int(line.split(" ")[1][:-1]))
43+
chars[character] = None
44+
if "SWIDTH" in line:
45+
swidth = (int(line.split(" ")[1]) / 1000) * size
46+
if "DWIDTH" in line:
47+
chars[character] = int(int(line.split(" ")[1]) + swidth)
48+
return chars
49+
50+
51+
def wrap(text, max_width, max_lines, font):
52+
# Used to wrap the title and description to avoid breaking the QR code
53+
lines = []
54+
ellipsis = 3 * font["."]
55+
line = ""
56+
line_width = 0
57+
for word in text.split(" "):
58+
for character in word:
59+
line_width += font[character]
60+
if (
61+
len(lines) + 1 != max_lines
62+
or sum(font[i] for i in word) + line_width <= max_width
63+
):
64+
if line_width > max_width:
65+
print(str(line_width) + line)
66+
line_width = sum(font[i] for i in word)
67+
lines.append(line.strip())
68+
line = word + " "
69+
break
70+
else:
71+
for char_1 in word:
72+
if line_width + ellipsis + font[char_1] > max_width:
73+
line = line + "..."
74+
print(str(line_width) + line)
75+
lines.append(line)
76+
return "\n".join(lines[:max_lines])
77+
line = line + char_1
78+
line_width += font[char_1]
79+
80+
else:
81+
line = line + word + " "
82+
83+
lines.append(line.strip())
84+
return "\n".join(lines[:max_lines])
85+
86+
87+
88+
# Get first 300 items, saving only the OSHWA UIDs. The first 300 are also used to find the
89+
# number of requests that will need to be made.
90+
# This was done this way since if the items themselves were all asked for and stored, the MagTag
91+
# would run out of memory. If we just got the number of total projects and chose a random number,
92+
# that also wouldn't work as you can only get individual projects with an OSHWA UID and these UIDs
93+
# are prefixed by the country they were registered in, thus making getting it with a simple number
94+
# in-between 1 and the total number of registered projects impossible.
95+
URL = "https://certificationapi.oshwa.org/api/projects?limit=300"
96+
97+
print(URL)
98+
99+
payload = {}
100+
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {TOKEN}"}
101+
102+
oshwaID = []
103+
104+
print("Getting number of projects and first set of 300 projects")
105+
with https.get(URL, headers=headers, data=payload) as response:
106+
R_JSON = response.json()
107+
total = int(R_JSON["total"])
108+
print(f"{total} Projects")
109+
for i in R_JSON["items"]:
110+
oshwaID.append(i["oshwaUid"])
111+
R_JSON.clear()
112+
R_JSON = None
113+
gc.collect()
114+
115+
# Gets the rest of the OSHWA UIDs
116+
print(len(oshwaID))
117+
for i in range(int(total / 300)):
118+
print(f"Getting request {i+2}")
119+
url = (
120+
f"https://certificationapi.oshwa.org/api/projects?limit=300&offset={3*(i+1)}00"
121+
)
122+
with https.get(url, headers=headers, data=payload) as response:
123+
R_JSON = response.json()
124+
for item in R_JSON["items"]:
125+
oshwaID.append(item["oshwaUid"])
126+
R_JSON.clear()
127+
R_JSON = None
128+
gc.collect()
129+
print(f"{len(oshwaID)} IDs gathered")
130+
131+
# Select the UID that will be displayed
132+
selected = random.choice(oshwaID)
133+
134+
# Get the project that will be displayed
135+
url = f"https://certificationapi.oshwa.org/api/projects/{selected}"
136+
response = https.get(url, headers=headers, data=payload)
137+
138+
selected = response.json()[0]
139+
140+
# Filters out characters that the API or the MagTag itself isn't handling correctly
141+
for char in range(1, 32):
142+
selected["projectDescription"].replace(chr(char), "")
143+
144+
selected["projectDescription"] = (
145+
selected["projectDescription"]
146+
.replace("&#x27;", "'")
147+
.replace("&amp;#x27;", "'")
148+
.replace("&#x2F;", "/")
149+
.replace("&quot;", '"')
150+
.replace("’", "'")
151+
)
152+
153+
# Add the two text fields
154+
magtag.add_text(
155+
text_font="fonts/Arial-12.bdf",
156+
text_position=(5, -2),
157+
text_scale=1,
158+
line_spacing=0.6,
159+
text_anchor_point=(0, 0),
160+
)
161+
162+
magtag.add_text(
163+
text_font="fonts/ArialMT-9.bdf",
164+
text_position=(5, 30),
165+
text_scale=1,
166+
line_spacing=0.6,
167+
text_anchor_point=(0, 0),
168+
)
169+
170+
# Create the QR code
171+
url = f"https://certification.oshwa.org/{selected['oshwaUid'].lower()}.html"
172+
magtag.graphics.qrcode(url, qr_size=4, x=173, y=3)
173+
174+
# Prepare to wrap the text correctly by getting the width of each character for every font
175+
arial_12 = font_width_to_dict("fonts/Arial-12.bdf")
176+
arial_9 = font_width_to_dict("fonts/ArialMT-9.bdf")
177+
178+
# Set the text. On some characters, this fails. If so, run the whole file again in 5 seconds
179+
try:
180+
magtag.set_text(wrap(selected["projectName"], 530, 2, arial_12), 0, False)
181+
magtag.set_text(wrap(selected["projectDescription"], 530, 10, arial_9), 1)
182+
magtag.exit_and_deep_sleep(3600)
183+
except Exception: # pylint: disable=broad-except
184+
print("Could not set title or description: unsupported glyphs.")
185+
print("Trying again in 10 seconds.")
186+
magtag.exit_and_deep_sleep(10)

0 commit comments

Comments
 (0)