A badger 2350 micro python app loading who has scaned my youandme.at qr code
0
youandme_badger_app.py
1import os
2import sys
3
4import urequests
5import wifi
6
7# The badger has a framework that loads in a lot of things like screen, image, wifi, etc
8# This string does not have the images, like the qr code but you can change those out for your own
9# Can read more about the apis here https://badgewa.re/docs
10
11# awkward, but loads the badge app directory for the qr code
12sys.path.insert(0, "/system/apps/badge")
13os.chdir("/system/apps/badge")
14
15# this state is stored to disk on exit of the app or sleep
16state = {"current_cursor": None, "cursor_stack": [], "show_messages": False, "limit": 2}
17
18handle_cache = {}
19rear_view = False
20
21USER_AGENT = "Bailey's badger"
22
23# Badger 2350: 264x176
24SCREEN_W = screen.width
25SCREEN_H = screen.height
26
27# settings for the "ui"
28BORDER = 4
29HEADER_H = 18
30LINE_H = 16
31PADDING = 6
32CONTENT_X = BORDER + PADDING
33CONTENT_RIGHT = SCREEN_W - BORDER - PADDING
34# Approximate character width for 14px font
35CHAR_W = 7
36# Max characters that fit in the content area per line
37MAX_CHARS_PER_LINE = (CONTENT_RIGHT - CONTENT_X) // CHAR_W
38
39# Hardcoded constellation to my url to look for at.youandme.connection records that has my did as a subject field
40CONSTELLATION_URL = "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks?subject=did%3Aplc%3Arnpkyqnmsw4ipey6eotbdnnf&source=at.youandme.connection%3Asubject"
41
42# Stuff to load in a png and load it into the framework
43qr_photo = image.load("qr_code.png")
44qr_window = image(qr_photo.width, qr_photo.height)
45qr_window.blit(qr_photo, vec2(0, 0))
46qr_window.dither()
47
48
49# Gets all the backlinks to my did from constellation. Takes into account pagination from state
50def get_constellation_links(direction):
51 url = CONSTELLATION_URL
52 # Hard coded limit 4, looks awkward cause I was originaly dynamically changing it
53 url += "&limit=4"
54 if direction == "next" and state["current_cursor"]:
55 state["cursor_stack"].append(state["current_cursor"])
56 url += f"&cursor={state['current_cursor']}"
57 elif direction == "back" and state["cursor_stack"]:
58 state["cursor_stack"].pop()
59 if state["cursor_stack"]:
60 url += f"&cursor={state['cursor_stack'][-1]}"
61
62 request = urequests.get(
63 url, headers={"Accept": "application/json", "User-Agent": USER_AGENT}
64 )
65 result = request.json()
66 state["current_cursor"] = result.get("cursor")
67 records = result["records"]
68 request.close()
69 return records
70
71
72# Calls slingshot to get the user's minidoc (a mini did doc) to resolve handles from the backlinks
73def get_handle(users_did):
74 if users_did in handle_cache:
75 return handle_cache[users_did]
76 url = f"https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier={users_did}"
77 request = urequests.get(
78 url, headers={"Accept": "application/json", "User-Agent": USER_AGENT}
79 )
80 result = request.json()
81 request.close()
82 handle = result.get("handle")
83 handle_cache[users_did] = handle
84 return handle
85
86
87# Makes a little border for a bit of a "ui"
88def draw_border():
89 screen.pen = color.black
90 for i in range(2):
91 screen.line(BORDER + i, BORDER, BORDER + i, SCREEN_H - BORDER)
92 screen.line(
93 SCREEN_W - BORDER - i, BORDER, SCREEN_W - BORDER - i, SCREEN_H - BORDER
94 )
95 screen.line(BORDER, BORDER + i, SCREEN_W - BORDER, BORDER + i)
96 screen.line(
97 BORDER, SCREEN_H - BORDER - i, SCREEN_W - BORDER, SCREEN_H - BORDER - i
98 )
99
100
101# Draws the header with the url
102def draw_header(page_num):
103 screen.pen = color.black
104 for row in range(BORDER + 2, BORDER + 2 + HEADER_H):
105 screen.line(BORDER + 2, row, SCREEN_W - BORDER - 2, row)
106
107 screen.pen = color.white
108 screen.text("youandme.at", CONTENT_X, BORDER - 4)
109 screen.text(f"pg {page_num}", CONTENT_RIGHT - 30, BORDER - 4)
110
111
112# Footer with some button text so I don't forget what button is which
113def draw_footer():
114 footer_y = SCREEN_H - BORDER - LINE_H - 10
115 screen.pen = color.black
116 screen.line(BORDER + 2, footer_y - 3, SCREEN_W - BORDER - 2, footer_y - 3)
117 screen.text("UP:back DOWN:next", CONTENT_X, footer_y)
118
119
120# Clears the eink screen to a white background (or turns off the dots)
121def clear_screen():
122 screen.pen = color.white
123 screen.clear()
124
125
126# Loads the font and makes sure the "pen" is set to black/on when drawing text/images, It's a global parameter
127# That needs to be set before drawing to the screen
128def before_write():
129 screen.pen = color.black
130 screen.font = pixel_font.load("/system/assets/fonts/manticore.ppf")
131
132
133# Build in function called from the frame work to update every cycle
134def update():
135 global rear_view
136 # Button checks before trying to connect to the wifi to get the value on wake up
137 go_to_next = badge.pressed(BUTTON_DOWN)
138 go_back = badge.pressed(BUTTON_UP)
139
140 if badge.pressed(BUTTON_B):
141 rear_view = not rear_view
142
143 if badge.pressed(BUTTON_C):
144 state["cursor_stack"] = []
145 state["current_cursor"] = None
146
147 clear_screen()
148 before_write()
149 # Rear view is the QR code (second screen), front is the list
150 if not rear_view:
151 screen.text("loading...", 100, 80)
152 # This tells the badge to update the screen
153 badge.update()
154
155 # Attempts to connect to wifi from the secrets file
156 wifi.connect()
157 while not wifi.tick():
158 pass
159 # Clears and resets the font/pen color before writing the ui
160 clear_screen()
161 before_write()
162
163 # Decides how to get the links for pagination
164 if go_to_next:
165 records = get_constellation_links("next")
166 elif go_back:
167 records = get_constellation_links("back")
168 else:
169 records = get_constellation_links("initial")
170
171 page_num = len(state["cursor_stack"]) + 1
172 # Draws the ui
173 draw_border()
174 draw_header(page_num)
175 draw_footer()
176
177 # Draws the list
178 y = BORDER + 2 + HEADER_H + PADDING
179
180 for i, record in enumerate(records):
181 if i > 0:
182 screen.pen = color.black
183 screen.line(CONTENT_X, y, CONTENT_RIGHT, y)
184 y += PADDING
185
186 screen.pen = color.black
187
188 handle_text = ""
189
190 try:
191 handle = get_handle(record["did"])
192 handle_text = f"@{handle}"
193 except:
194 handle_text = record["did"][:20]
195
196 # Line 1: @handle
197 screen.text(handle_text, CONTENT_X, y)
198 y += LINE_H
199
200 y += PADDING
201 else:
202 # If it's the rear screen just write the qr code to the badge
203 screen.blit(qr_window, vec2(SCREEN_W / 2 - qr_photo.width / 2, 10))
204 # Updates the badge and tell it to wait for the RTC clock update or a button press
205 badge.update()
206 wait_for_button_or_alarm(timeout=5000)
207
208
209# saves state to file system on exit
210def on_exit():
211 State.save("pumpkin", state)
212
213
214# Start up and loads the state
215State.load("pumpkin", state)
216run(update)