Reusable how-to for generating a printable PDF with a Wi-Fi QR code so guests can scan-to-join, produced entirely on the Mac with no extra system tooling. Verified working 2026-07-31.

For Agents

Pipeline: pull ssid+key from the telep-router via uci show wireless → build an HTML card with an inline SVG QR (segno) → render to PDF with headless Google Chrome → print. Never write the actual Wi-Fi passwords into this vault. Read them live from the router at print time.

Wi-Fi QR payload format

A Wi-Fi QR code is just a plain-text string in this format:

WIFI:T:<auth>;S:<ssid>;P:<password>;;
  • T: — auth type. Use WPA. It is the widely-compatible value and modern phones join WPA3/SAE networks from a T:WPA payload fine. (nopass for open networks; WEP is legacy.)
  • S: — SSID.
  • P: — password.
  • Note the double semicolon at the end.
  • Escape any of \ ; , : " appearing in the SSID or password with a leading backslash.

You rarely hand-build this string — segno.helpers.make_wifi(...) does it for you (see below).

Where the network details come from

The source of truth is the OpenWrt router, not this note. SSH in and read UCI:

/usr/bin/ssh root@100.69.112.32   # telep-router, over the tailnet (was 100.115.194.51 before the 2026-08-03 reset)
uci show wireless                  # ssid / key / encryption per radio
NetworkSSIDEncryptionNotes
Maintelep1sae (WPA3)Same SSID on both radios (radio0 5 GHz + radio1 2.4 GHz). Use T:WPA in the payload.
Camerastelep-ccpsk2 (WPA2)Isolated VLAN. PSK is flagged leaked — rotate it before printing a QR for it.

See WiFi for the radio layout and Camera VLAN (telep-cc) for the camera network.

The pipeline

1. Throwaway venv + segno

There is no qrencode on this Mac. Python is python3.14 and externally-managed (PEP 668), so system-wide pip install is blocked. Use a disposable venv — segno is pure Python (no C deps):

python3 -m venv venv
venv/bin/pip install segno

2. Build the HTML card

segno.helpers.make_wifi builds the QR; render it as an inline SVG into an A4 HTML card with the SSID title, a scan to join caption, and the password in monospace so people can also type it.

import io
import segno
from segno import helpers
 
ssid = "telep1"
password = "<read from: uci show wireless>"   # do NOT hardcode / commit
 
qr = helpers.make_wifi(ssid=ssid, password=password, security="WPA")
 
buf = io.BytesIO()                 # segno's SVG writer emits BYTES
qr.save(buf, kind="svg", scale=8, border=2)
svg = buf.getvalue().decode("utf-8")
 
html = f"""<!doctype html><meta charset="utf-8">
<style>
  @page {{ size: A4; margin: 0; }}
  body {{ margin: 0; font-family: -apple-system, Helvetica, sans-serif; }}
  .card {{ height: 100vh; display: flex; flex-direction: column;
           align-items: center; justify-content: center; gap: 1.2rem; }}
  h1 {{ font-size: 3rem; margin: 0; }}
  .cap {{ font-size: 1.4rem; color: #444; }}
  .pw {{ font-family: ui-monospace, Menlo, monospace; font-size: 1.6rem; }}
  svg {{ width: 55vw; height: auto; }}
</style>
<div class="card">
  <h1>{ssid}</h1>
  {svg}
  <div class="cap">Scan to join Wi-Fi</div>
  <div class="pw">{password}</div>
</div>"""
 
with open("sheet.html", "w") as f:
    f.write(html)

3. Render to PDF with headless Chrome

There is no wkhtmltopdf, but Google Chrome is installed and prints HTML to PDF headless:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --headless --disable-gpu --no-pdf-header-footer \
  --print-to-pdf=out.pdf "file://$(pwd)/sheet.html"

4. Verify, then print

Open out.pdf (open -a Preview out.pdf, or the Read tool renders it). Scan it once with a phone before printing a batch — especially for telep1, which is WPA3-only.

Gotchas

PEP 668 — do not --break-system-packages

python3.14 here is externally-managed; system pip install is blocked. Use a throwaway venv, never --break-system-packages.

segno SVG writer returns bytes

Save SVG into io.BytesIO() then .getvalue().decode("utf-8"). Using io.StringIO() raises TypeError: string argument expected, got 'bytes'.

T:WPA also covers WPA3

A WIFI:T:WPA;... payload joins WPA3/SAE networks (like telep1) on modern phones. There is no separate T:WPA3 value worth using — WPA is the compatible choice.