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("&#x27;", "'")
150      .replace("&amp;#x27;", "'")
151      .replace("&#x2F;", "/")
152      .replace("&quot;", '"')
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)