4.8 KiB
| created | path | project | tags | type | |
|---|---|---|---|---|---|
| 2026-06-28 | Sources/Homelab | wifi-join-webapp |
|
project-plan |
Goal
Replace the ten-click LuCI ceremony for joining a new WiFi network on the road with a one-screen, browser-based scan-and-join page. Universal access (phone/tablet/laptop, no SSH, no app), served off the all-in-one travel Pi. Tap a network, enter the password, done — the page writes the travelmate uplink and reconnects behind the scenes.
Secondary: it's a genuine portfolio piece — a custom web tool talking to OpenWRT's ubus/UCI API.
Problem
Current join flow is all manual LuCI: type router IP (browser never remembers it) → log in → open travelmate → navigate to its page → scan radio0 → find network → add → apply → wait. Interface is fine; the ceremony is the problem. Most of the pain is front-half (reach + login + navigation depth) plus a scan/add/apply tail.
Deep-link bookmark + password-manager autofill would kill the front half cheaply (considered as Option A), but the decision was to build the real thing — a custom page that collapses the entire flow.
Locked Decisions
- Architecture: thin backend shim (not pure-frontend-against-ubus). Privileged work (write config + reconnect travelmate) stays server-side in one place; avoids wrestling rpcd/ubus ACLs from browser JS. Frontend just hits two simple endpoints.
- Runs on the all-in-one travel Pi. OpenWRT + travelmate + Pi-hole are all on the one Raspberry Pi, so the shim talks to ubus/UCI locally — same-origin, no CORS, no cross-box auth.
- Backend language: lua (native to OpenWRT, uhttpd speaks it, zero extra packages). Python kept in reach as fallback if travelmate config-writing gets gnarly in lua. Travis is new to lua — pace accordingly.
- Rides existing uhttpd (already running for LuCI). No new web server.
- Backup before any router change (Phase 0, same rule as the captive-portal plan — same router).
- Build + curl-test the shim before any frontend. Prove backend, then UI is trivial.
Open Items
- Verify exact
config wifi-configfield shape against live/etc/config/travelmatebefore trusting the join write (don't write from memory). - Decide whether a
/statusendpoint is worth it for "waiting to connect" feedback, or poll something simpler. - ubus ACL: confirm the shim can call iwinfo scan + uci commit + travelmate restart under whatever user uhttpd/rpcd runs it as.
- Decide auth on the page itself (LAN-only? any gate?) — currently assumed trusted-LAN, no extra auth.
Phases
Phase 0 — Backup (non-negotiable, first)
sysupgrade -b /tmp/backup-$(date +%Y%m%d).tar.gzscptarball off the Pi to laptop; confirm present before any change
Phase 1 — Scan endpoint
- lua shim:
GET /cgi-bin/wifi-join/scan→ triggers radio0 scan, returns JSON[{ssid, signal, encryption}] - curl-test from laptop; prove ubus scan works before any UI
Phase 2 — Join endpoint
- Reference live
cat /etc/config/travelmatefor exact uplink shape - lua shim:
POST /cgi-bin/wifi-join/joinbody{ssid, key}→ writes travelmatewifi-configuplink, commits, reconnects - curl-test headless; prove travelmate write + reconnect works
Phase 3 — Frontend (one page)
- Vanilla HTML/JS single file served by uhttpd: Scan button → network list w/ signal → tap → password field → Join → status
- Wire to the two proven endpoints
Phase 4 — Polish
- Signal bars, connect-status feedback
- Bookmark target; effectively replaces LuCI for the common case
- Optional
/statusendpoint if feedback needs it
Notes
- Architecture:
Browser ──HTTP same-origin──► uhttpd ──► static frontend (one HTML/JS page) └──/cgi-bin/wifi-join──► lua shim ├─ ubus call iwinfo scan ├─ uci set wireless / travelmate wifi-config ├─ uci commit └─ /etc/init.d/travelmate restart - travelmate wrinkle: 2.1.3 stores uplinks as
config wifi-configsections in/etc/config/travelmate, NOT rawwireless.@wifi-iface. "Join" = add a travelmate uplink entry and let its interface handler pick it up. Version-specific; verify against live config. - Environment (confirmed this session): all-in-one Pi. travelmate
2.1.3-r3, luci-app-travelmate25.157.41932~3b521ef, OpenWRT24.10.1r28597. - Build discipline: backend proven by curl before a line of frontend. Same one-step-at-a-time interactive mode for the shim work.
- Related plan:
travel-pi-router(captive-portal fix) — same Pi, shares the Phase 0 backup rule. This webapp is a separate deliverable from the captive-portal hook but lives on the same box.