import os import sys import urequests import wifi # The badger has a framework that loads in a lot of things like screen, image, wifi, etc # This string does not have the images, like the qr code but you can change those out for your own # Can read more about the apis here https://badgewa.re/docs # awkward, but loads the badge app directory for the qr code sys.path.insert(0, "/system/apps/badge") os.chdir("/system/apps/badge") # this state is stored to disk on exit of the app or sleep state = {"current_cursor": None, "cursor_stack": [], "show_messages": False, "limit": 2} handle_cache = {} rear_view = False USER_AGENT = "Bailey's badger" # Badger 2350: 264x176 SCREEN_W = screen.width SCREEN_H = screen.height # settings for the "ui" BORDER = 4 HEADER_H = 18 LINE_H = 16 PADDING = 6 CONTENT_X = BORDER + PADDING CONTENT_RIGHT = SCREEN_W - BORDER - PADDING # Approximate character width for 14px font CHAR_W = 7 # Max characters that fit in the content area per line MAX_CHARS_PER_LINE = (CONTENT_RIGHT - CONTENT_X) // CHAR_W # Hardcoded constellation to my url to look for at.youandme.connection records that has my did as a subject field CONSTELLATION_URL = "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks?subject=did%3Aplc%3Arnpkyqnmsw4ipey6eotbdnnf&source=at.youandme.connection%3Asubject" # Stuff to load in a png and load it into the framework qr_photo = image.load("qr_code.png") qr_window = image(qr_photo.width, qr_photo.height) qr_window.blit(qr_photo, vec2(0, 0)) qr_window.dither() # Gets all the backlinks to my did from constellation. Takes into account pagination from state def get_constellation_links(direction): url = CONSTELLATION_URL # Hard coded limit 4, looks awkward cause I was originaly dynamically changing it url += "&limit=4" if direction == "next" and state["current_cursor"]: state["cursor_stack"].append(state["current_cursor"]) url += f"&cursor={state['current_cursor']}" elif direction == "back" and state["cursor_stack"]: state["cursor_stack"].pop() if state["cursor_stack"]: url += f"&cursor={state['cursor_stack'][-1]}" request = urequests.get( url, headers={"Accept": "application/json", "User-Agent": USER_AGENT} ) result = request.json() state["current_cursor"] = result.get("cursor") records = result["records"] request.close() return records # Calls slingshot to get the user's minidoc (a mini did doc) to resolve handles from the backlinks def get_handle(users_did): if users_did in handle_cache: return handle_cache[users_did] url = f"https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier={users_did}" request = urequests.get( url, headers={"Accept": "application/json", "User-Agent": USER_AGENT} ) result = request.json() request.close() handle = result.get("handle") handle_cache[users_did] = handle return handle # Makes a little border for a bit of a "ui" def draw_border(): screen.pen = color.black for i in range(2): screen.line(BORDER + i, BORDER, BORDER + i, SCREEN_H - BORDER) screen.line( SCREEN_W - BORDER - i, BORDER, SCREEN_W - BORDER - i, SCREEN_H - BORDER ) screen.line(BORDER, BORDER + i, SCREEN_W - BORDER, BORDER + i) screen.line( BORDER, SCREEN_H - BORDER - i, SCREEN_W - BORDER, SCREEN_H - BORDER - i ) # Draws the header with the url def draw_header(page_num): screen.pen = color.black for row in range(BORDER + 2, BORDER + 2 + HEADER_H): screen.line(BORDER + 2, row, SCREEN_W - BORDER - 2, row) screen.pen = color.white screen.text("youandme.at", CONTENT_X, BORDER - 4) screen.text(f"pg {page_num}", CONTENT_RIGHT - 30, BORDER - 4) # Footer with some button text so I don't forget what button is which def draw_footer(): footer_y = SCREEN_H - BORDER - LINE_H - 10 screen.pen = color.black screen.line(BORDER + 2, footer_y - 3, SCREEN_W - BORDER - 2, footer_y - 3) screen.text("UP:back DOWN:next", CONTENT_X, footer_y) # Clears the eink screen to a white background (or turns off the dots) def clear_screen(): screen.pen = color.white screen.clear() # Loads the font and makes sure the "pen" is set to black/on when drawing text/images, It's a global parameter # That needs to be set before drawing to the screen def before_write(): screen.pen = color.black screen.font = pixel_font.load("/system/assets/fonts/manticore.ppf") # Build in function called from the frame work to update every cycle def update(): global rear_view # Button checks before trying to connect to the wifi to get the value on wake up go_to_next = badge.pressed(BUTTON_DOWN) go_back = badge.pressed(BUTTON_UP) if badge.pressed(BUTTON_B): rear_view = not rear_view if badge.pressed(BUTTON_C): state["cursor_stack"] = [] state["current_cursor"] = None clear_screen() before_write() # Rear view is the QR code (second screen), front is the list if not rear_view: screen.text("loading...", 100, 80) # This tells the badge to update the screen badge.update() # Attempts to connect to wifi from the secrets file wifi.connect() while not wifi.tick(): pass # Clears and resets the font/pen color before writing the ui clear_screen() before_write() # Decides how to get the links for pagination if go_to_next: records = get_constellation_links("next") elif go_back: records = get_constellation_links("back") else: records = get_constellation_links("initial") page_num = len(state["cursor_stack"]) + 1 # Draws the ui draw_border() draw_header(page_num) draw_footer() # Draws the list y = BORDER + 2 + HEADER_H + PADDING for i, record in enumerate(records): if i > 0: screen.pen = color.black screen.line(CONTENT_X, y, CONTENT_RIGHT, y) y += PADDING screen.pen = color.black handle_text = "" try: handle = get_handle(record["did"]) handle_text = f"@{handle}" except: handle_text = record["did"][:20] # Line 1: @handle screen.text(handle_text, CONTENT_X, y) y += LINE_H y += PADDING else: # If it's the rear screen just write the qr code to the badge screen.blit(qr_window, vec2(SCREEN_W / 2 - qr_photo.width / 2, 10)) # Updates the badge and tell it to wait for the RTC clock update or a button press badge.update() wait_for_button_or_alarm(timeout=5000) # saves state to file system on exit def on_exit(): State.save("pumpkin", state) # Start up and loads the state State.load("pumpkin", state) run(update)