A badger 2350 micro python app loading who has scaned my youandme.at qr code
0
youandme_badger_app.py
216 lines 7.1 kB view raw
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)