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