release: v0.3.0#52
Merged
Merged
Conversation
- Trim length (~485 → ~370 lines). Fold the "Features" bullet list into the three-tier intro; cut the exhaustive project-structure dump and the duplicated Makefile table. - Bump the numbers: 9 TUI categories / 53 native tools / 997 device tests / 117 frontend tests. Refresh the TUI feature highlights (ADS-B global basemap, Telegram, Watch Dogs Go, ROM Launcher, shared launcher helper). - Add a new "Polling and data flow" section with three Mermaid flowcharts: device→cloud telemetry timer (the only real polling loop), cloud dashboard reads (no polling — RSC on page load), and local webdash (on-demand + SSE only while Live Monitor is open). - Call out the new CONFIG → Push Interval "off" option and document how to opt out of cloud telemetry entirely. - Preserve all screenshot images. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
git_sync committed without checking blob sizes. When a 164MB .platformio tarball landed in ~ via backup.sh all, the nightly push failed silently for 3 days while commits stacked locally on the device. Add git_sync_guard_blob_size: scans the staged index for any blob over 95MB, names each offender by size and path, and aborts the sync before commit. Offending files are unstaged so the working tree stays clean for the next attempt. .gitignore remains the single source of truth for "don't back this up"; this guard only catches "can't push what we staged." Tested: small file passes silently, 96MB file caught by name, returns 1 with log_entry "ABORTED: oversize blob in index".
Package the 4 battery-safety services that were previously created
by hand pointing at ~/scripts/. After the pkg migration that path
no longer exists, so the units have been silently failing on every
boot — low-battery-shutdown retrying every 10s without ever
protecting the pack, pmu-voltage-min never setting VOFF to 2.9V.
This let the device brownout mid-sleep-wake when on battery.
Adds:
packaging/systemd/pmu-voltage-min.service
packaging/systemd/low-battery-shutdown.service
packaging/systemd/cpu-freq-cap.service
packaging/systemd/crash-log.service
device/scripts/power/battery-safety.sh (on|off|status helper)
All four ExecStart paths now point at /opt/uconsole/scripts/...
build-deb.sh picks them up automatically via the systemd/* glob.
UNSTABLE / opt-in semantics:
- Fresh installs: units present but NOT enabled. User opts in with
`battery-safety.sh on` or `systemctl enable --now`.
- Upgrades: wants-target symlinks survive file replacement, so
previously-enabled units stay enabled; just clears accumulated
failed-state and restarts the daemon so the fixed ExecStart runs.
- Cleans up orphaned ~/scripts/*.sh compat symlinks from the hand
fix.
Untested in combination with the packaged layout — shipping to dev
for real-device verification before promoting to main. Do not cut
a release off this until we've confirmed the enable path works on
a pristine device.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a GPS-tagged war-driving feature spanning the TUI, webdash, and a demo data generator. Landing on a WIP branch because the UX isn't production-ready yet. ## What's in the bundle ### TUI (device/lib/tui/marauder.py) - New "War Drive" tile in the Marauder menu (index 7) - Continuous `scanap` with per-sighting CSV logging to `~/esp32/marauder-logs/wardrive-<ts>.csv` (WiGLE-ish schema) - Persistent gpspipe poller thread maintains live TPV/SKY state - Two views: braille-canvas map (APs + walked track + crosshair) and scrolling list; Tab/M/D to toggle - Pulsing `● LOGGING` state banner, elapsed/rate/file-size header - Exit confirmation modal with session summary + webdash URL - Pause (X) / save-and-exit (B) with summary gate ### Webdash (device/webdash/app.py + templates/) - GET /wardrive — MapLibre GL native-layer map with Carto Dark Matter basemap, heatmap + circle layers, click-to-popup, mobile-first DaisyUI (synthwave) navbar + FAB controls. No deck.gl, no Tailwind runtime. - GET /wardrive?basic=1 — Leaflet fallback preserved at wardrive_basic.html - GET /api/wardrive/sessions — list CSVs, newest first - GET /api/wardrive/data/<name>?since=<idx> — delta-poll parsed rows - Per-route CSP: adds basemaps.cartocdn.com, fonts.googleapis.com, jsdelivr.net, worker-src blob: for MapLibre - Service Worker updated to never cache /wardrive and to invalidate when any template changes (not just app.py mtime) ### Demo generator (device/scripts/util/wardrive-demo.py) - `static [min]` writes a complete CSV with a realistic walking path and scattered APs — use for UI testing indoors - `live [min]` appends rows in real time (~1 Hz) to test the polling loop end-to-end - `clean` removes DEMO-prefixed CSVs ## Feature flag (DISABLED BY DEFAULT) All /wardrive and /api/wardrive/* routes return 404 unless one of: - file /etc/uconsole/wardrive-enabled exists - env UCONSOLE_WARDRIVE_ENABLED=1 in the webdash service To enable on this device: sudo touch /etc/uconsole/wardrive-enabled sudo systemctl restart uconsole-webdash ## Why WIP - Map UX is still being iterated on (pillars were bad, heatmap+dots feels better but isn't final) - Performance on the CM4-class GPU needs more measurement - TUI map view's Overpass street overlay is planned but not built - Mobile responsive pass is functional but could use more polish - No coverage analysis, KML export, or Wigle upload yet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a real street background to the TUI War Drive map using OSM highway geometry fetched from the Overpass API in the background. **_OsmStreetFetcher** (new class, module-level) - Persistent daemon thread watching live GPS fix - Fetches highway ways in a 500 m radius around the current position with a 25 s Overpass timeout - Caches responses to ~/esp32/marauder-logs/osm-streets-cache.json keyed by GPS rounded to 3 decimals (~110 m); bounded to 20 entries - Re-fetches when position drifts >200 m from last query center, or when the cached entry is older than 1 hour - Retries failed fetches after 60 s; otherwise idles 15 s - Works fully offline once a neighborhood is cached **_draw_wardrive_map** (updated) - New `streets=` kwarg; when provided, draws polylines on a secondary braille canvas rendered underneath the APs/track/crosshair layer - Per-cell merge — if the same cell contains both street bits and data bits, data color wins (bright green); else street bits render in C_DIM with A_DIM attribute for a muted gray backdrop - Viewport auto-fit now includes first/last point of each street polyline, so the map is anchored to the neighborhood even with no AP sightings yet - Scale caption shows street segment count **_wardrive** main loop - Instantiates _OsmStreetFetcher, starts on entry, stops in finally - Pushes each GPS track sample to fetcher.update_position() - New `S` key toggles streets on/off; footer reflects state - Status bar shows "fetching…" on first enable and surfaces errors **Smoke test (real network):** - Around 40.7141,-73.9928 (Lower East Side) returned 1,391 polylines in under 5 s, cache file 175 KB on disk. Subsequent sessions in the same neighborhood hit the cache immediately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops Manhattan LES payload from 1,391 polylines to 249 (-82%) and
cache from 175 KB to 41 KB (-76%). Renders in two tiers so the grid
reads clearly in a dense city.
**Query-level filter** (Overpass):
- [highway~"^(motorway|trunk|primary|secondary|tertiary|unclassified|
residential|living_street|*_link)$"] drops footways, sidewalks,
cycleways, service roads, pedestrian zones. These are noise in a
TUI overview.
- "out geom tags;" returns highway type alongside geometry.
**Tiered rendering**:
- MAJOR_TYPES (motorway/trunk/primary/secondary + links) → medium
C_DIM so avenues and arterials stand out
- MINOR_TYPES (tertiary/unclassified/residential/living_street) →
C_DIM | A_DIM so cross streets recede
- Data (APs/track/crosshair) on top at bright C_OK as before
- Per-cell priority: data > major > minor
**Viewport culling**:
- Quick lat/lon bbox test per polyline before projection
- Skips drawing for anything entirely off-canvas (plus 10% padding)
- Material win on dense neighborhoods where the 500m radius
overshoots the visible view at high zoom
**Cache tuning for city use**:
- MAX_AGE_S: 1 h → 7 days (urban street grid rarely changes)
- MAX_CACHE_ENTRIES: 20 → 50 (city walks cover more unique bboxes)
- MOVE_THRESHOLD_M: 200 → 220 (~2-3 NYC blocks)
- Backward-compatible loader accepts legacy flat-polyline entries
from v1 cache
**Scale caption** now shows "N_major / N_total major" count for
awareness.
Smoke test around 40.7141,-73.9928 (LES):
1,391 → 249 polys; 175 → 41 KB cache.
Types: {residential:104, secondary:70, primary:71, motorway:1}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a HARDWARE → Meshtastic submenu that drives the locally-running meshtasticd daemon over TCP 4403 via the official meshtastic Python CLI. No bespoke chat protocol — entries map to status/nodes/listen/send/web/ logs and service start/stop/restart. Keeps the LoRa submenu intact for point-to-point SX1262 use, with a note that meshtasticd must be stopped first (both want exclusive SPI1). New: scripts/radio/meshtastic.sh — thin bash wrapper around the CLI.
meshtastic --listen dumps raw protobuf + DEBUG log spam that's unreadable in the TUI. Pipe through a small python filter that prints one line per packet: [HH:MM:SS] PORTNUM from=!nodeid plus MSG: <text> for TEXT_MESSAGE_APP. Raw view still available via 'meshtastic --host localhost --listen' directly.
… entry
HARDWARE now has one "LoRa Mesh" submenu that drills into:
• Chat (web UI), Send, Nodes, Listen, Status — the common actions
• Config ▶ — privacy presets (stealth/public), MQTT on/off/toggle,
position off/low/full/clear, rename, region, channel-name
• Service ▶ — start/stop/restart/logs/web URL
• Direct LoRa (P2P) ▶ — the old lora.sh point-to-point entries,
kept for advanced users who stop meshtasticd
Extends meshtastic.sh with a `config` subcommand covering the full
privacy + radio settings surface (mqtt, position, rename, region,
channel-name, and the privacy stealth/public macro presets).
Replaces the previous split where LoRa and Meshtastic each had their
own HARDWARE entry — with meshtasticd owning SPI1 in practice, the
split produced confusion and dead entries.
Reuses the ADS-B basemap renderer + projection to plot mesh nodes from `meshtastic --host localhost --info` on the same curses braille canvas. Home position is shared with ADS-B config. Controls: +/- zoom, j/k cycle visible nodes, l toggle labels, b toggle basemap overlay, h set home from GPS, r refresh, q quit. Caches the nodes JSON for 30s; re-fetches in background.
Reviewed meshtastic.org/docs and reshaped the wrapper to mirror the canonical CLI surface rather than my guessed shape: * send — add send-dm (<!nodeid> <msg>), send-ack (--ack), send-ch (--ch-index N --sendtext). Rename "Send Message" → "Broadcast" + add "Direct Message" / "Broadcast + ACK" / "Send on Channel" TUI entries. * reply — expose `meshtastic --reply` listen+echo mode. * position low — use precision=13 (~2.9 km, the docs-canonical example) instead of an ad-hoc 10. Applies to privacy public preset. * channel — full channel management: list, add (secondary), del (idx>0), psk (none|default|random|<hex>). New sub:lora_channels. * power — reboot / shutdown / factory-reset (factory-reset requires typing RESET to confirm). New sub:lora_power. * usage text annotates each block with the Meshtastic doc reference (canonical flags, channel-slot model, mqtt.*/position.*/lora.* keys). The TUI sub:lora_mesh is now the single entry point covering every canonical operation docs describe — message send variants, channels, config, service, power, and direct LoRa P2P as the escape hatch.
Design doc for replacing the hardcoded MIMI_IP with serial-based IP discovery, adding a WiFi config panel (scan / copy-from-uConsole / manual), and distilling the ESP32 submenus (−4 top-level items per mode, Reflash/War Drive/Settings subfolds for destructive or growable ops). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles current working-tree changes onto wip/wardrive-map so that filter-repo can run against a clean tree. Includes wardrive map, meshtastic map updates, adsb basemap work, webdash wardrive backend, MimiClaw TUI module (untracked), and battery-safety edits. Unpackable via 'git reset --soft HEAD~1' after the purge completes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
device/scripts/system/backup.sh wrote WiFi PSKs, SSH keys, and sudoers rules into whatever repo it was invoked from. When an automated run picked it up from this public tree on 2026-04-17 (commit 0881682), it committed five .nmconnection files with plaintext PSKs to origin/dev. Root cause: the script existed in two places — this public repo and the private ~/pkg repo. It belongs only in ~/pkg. Fix (defense-in-depth): - Delete backup.sh from this repo entirely. One source of truth. - Gitignore device/scripts/system/wifi/ and other credential-bearing paths so future accidents can't recommit them. - History has been rewritten via git-filter-repo to purge the WiFi .nmconnection files from every commit on every ref. PSKs should be considered compromised (public for ~5 days on GitHub). Rotation is a separate concern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous launcher searched two trees (/opt/uconsole/lib AND ~/uconsole-cloud/device/lib), preferring the dev tree. That made "where is this TUI actually running from?" ambiguous and coupled the launcher to a specific dev-tree path that only exists on the author's machine. New behaviour: read from ~/pkg/lib if it exists, else fall back to /opt/uconsole/lib. Switching between stable and in-flight TUI code is now a git branch switch in ~/pkg/ — no env vars, no precedence rules, no second search path. On boxes without ~/pkg/ (ordinary APT-only installs) nothing changes: /opt/uconsole/lib is still the active tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Meshtastic integration for v0.2.2: TUI submenu, mesh map, LoRa/Meshtastic consolidation under a single HARDWARE entry, one-line packet-summary filter for the Listen view, and a meshtasticd wrapper aligned with the canonical Meshtastic docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous logic ran 'git describe --tags --always' against _PKG_ROOT and parsed the result. When _PKG_ROOT is ~/pkg (the new default after the console launcher refactor), that reads ~/pkg's tags — which do not track uconsole-cloud releases. Result: TUI showed '0.1.6-dev' while VERSION said 0.2.1. Fix: read VERSION unconditionally. Append '-dev' when running from any non-installed tree. Simpler, correct, and survives future tag drift in whichever repo hosts the TUI source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier commit (0d36d98) moved the launcher to prefer ~/pkg/lib over /opt/uconsole/lib. That gave ~/pkg a runtime role it was never meant to have, and created the version-string bug fixed in 2d81b91: pkg has its own git tags that don't track releases, and anything reading metadata from pkg got the wrong answer. Back to one runtime source: /opt/uconsole/lib. Pkg is private backup only — it holds WiFi/SSH creds and package manifests, never runtime code consumers should import from. For ad-hoc testing of an alternate tree, set UCONSOLE_DEV_LIB=/path. No implicit dev override — what runs is always what `make install` last deployed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sibling to console-pkg. Sets UCONSOLE_DEV_LIB to ~/uconsole-cloud/device/lib so the TUI loads source code from the active dev checkout instead of the installed /opt/uconsole tree. Use case: preview uncommitted or pre-install edits without running make install. Intended to be bound to Ctrl+\` at the compositor layer while Ctrl+Shift+P stays on console-pkg (installed/published TUI). Falls back to installed if the dev tree is missing (e.g. on end-user devices without the source checkout). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VERSION tracks the last released version. Dev work is always building toward the next one, so the running TUI should report where the work is headed — not where the last release sat. 0.2.1 → 0.2.2-dev. Falls back to plain '-dev' suffix if VERSION has a non-numeric tail (pre-release tags like 0.2.1-beta etc.) so we never crash on version parsing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three high-impact fixes aligning CONTRIBUTING.md with the current architecture: 1. Versioning section was describing a git-describe-based scheme that was replaced today (commit ab2a3f6). Now documents VERSION-file + patch-bump semantics so contributors understand why console-dev shows 0.2.2-dev while the last release was 0.2.1. 2. Device development section now names the three launchers (console-dev / console-pkg / console) and their keybinds, so contributors know which one to use for the iteration loop without reading source. 3. TUI module list grew from 11 entries to 22 — the old list missed mimiclaw, telegram, watchdogs, meshtastic_map, launcher, and five adsb files. Missing modules made the "what does this file do?" question unanswerable from the docs alone. Does not touch script count, test count, or tool count — those need a separate recount pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three changes to reduce root-level sprawl and document the full release flow: 1. New docs/PIPELINE.md — maintainer reference for the edit → preview → commit → CI → publish → user chain. Not contributor-facing (that's CONTRIBUTING.md's job). Covers the three speeds (flicker/commit/ release), automation triggers, manual decision points, and failure modes. 2. Move FEATURES.md → docs/FEATURES.md. Updated the 5 references in CONTRIBUTING.md, SECURITY.md, and three Claude slash-commands. 3. Move ADSB_BASEMAP_PLAN.md → docs/plans/ADSB_BASEMAP_PLAN.md. No references existed; this is a historical feature plan that now lives alongside any future plans in a single directory. Root now holds only the docs GitHub conventions expect there: README.md, CONTRIBUTING.md, LICENSE, SECURITY.md, CHANGELOG.md, VERSION. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of "ESP32 hub shows Unknown with MimiClaw flashed": 1. MimiClaw detection branch + TUI module lived only on wip/wardrive-map (commit d85afc6), never merged to dev. Installed /opt/uconsole/lib had no MIMICLAW enum value, no mimiclaw.py, and no menu wiring. 2. Even the wip branch's probe strings were wrong — checked for "mimi:" and "Type 'help'" but the real firmware emits "mimi>" as its prompt after any newline. Evidence (device at /dev/ttyACM0 running MimiClaw v8390390 today): Ctrl-C×2 + \r\n → '\r\nmimi> \r\nmimi> ' ← marker present in the same response Phase 1 already reads Fix is surgical — cherry-picked from wip without pulling in unrelated WIP work (wardrive menu additions, GUI launcher helpers, version regression): - device/lib/tui/mimiclaw.py (369 lines, new) Chat / Serial / Status / Flash handlers. - device/lib/tui/esp32_detect.py (+9 lines) MIMICLAW enum value + Phase 2.5 probe that checks for "mimi>" in the existing Phase 1 response. No extra serial round-trip. - device/lib/tui/framework.py (+~30 lines) _ESP32_MIMICLAW_ITEMS submenu list + dispatcher entries (_mimiclaw_chat, _mimiclaw_serial, _mimiclaw_status, _esp32_force_mc) + badge + flash picker entry. Verified end-to-end: detect() → Firmware.MIMICLAW tui.mimiclaw.run_mimiclaw_{chat,serial,status,flash} all importable Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MIMI_IP was hardcoded to 192.168.1.23. Breaks every time DHCP reassigns,
and broke today when the device roamed to a different AP and picked up a
different address. The spec at
docs/specs/2026-04-22-mimiclaw-wifi-and-esp32-tui-refactor.md Part 1
called for cache + serial-probe fallback. This implements exactly that
scope, no more.
What changed in tui/mimiclaw.py:
- Delete MIMI_IP constant.
- Add _load_cached_ip / _save_ip — JSON at
~/.config/uconsole/mimiclaw.json, chmod 600, atomic tmp+rename.
- Add _probe_ip_via_serial — sends 'wifi_status', parses 'IP: x.y.z.w',
returns None on 0.0.0.0 or any parse failure. Short-lived serial
matching the existing _query_mimiclaw_status pattern.
- Add _resolve_ip(prefer_fresh=False) — cache → probe → None.
- Rewrite connect_ws in run_mimiclaw_chat:
- First attempt uses cache (fast path)
- On connect failure, re-probe serial once and retry
- On second failure: surface the real error ("device offline" or
"connection failed: <reason>") instead of a generic timeout
Verified end-to-end against live MimiClaw at 192.168.1.23 on
Big Parma - 2.4GHz: unit tests pass (5/5 including 600-perm check,
junk rejection, live probe, cache population) and resolved IP is
correct.
Does NOT implement Part 2 (WiFi config panel / Settings subfold).
That's a separate commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a WiFi config screen reachable from MimiClaw ▸ Settings ▸ WiFi,
completing Part 2 of docs/specs/2026-04-22-mimiclaw-wifi-and-esp32-
tui-refactor.md.
Method picker with four flows:
- Scan: send wifi_scan, parse '[N] SSID=X RSSI=Y CH=Z Auth=W' lines,
sort by RSSI desc, de-dup empties, render list with signal bars
and lock glyph for secured networks.
- Copy from uConsole: sudo nmcli to read the device's currently
active WiFi SSID + PSK, confirm, apply.
- Manual: two text fields (SSID allows spaces, password masked with
Ctrl-R reveal).
- Disconnect: sends set_wifi "" "" with confirm. Behaviour on real
firmware is empirically untested; surfaces any error plainly.
Apply pipeline (shared across all four flows):
1. set_wifi + wait ≤3s for "WiFi credentials saved for SSID:"
2. restart + 10s progress screen
3. poll wifi_status every 2s for up to 15s, looking for real IP
4. on success: update ~/.config/uconsole/mimiclaw.json cache
5. on failure: surface specific error (port busy / timeout /
SSID out-of-range etc) — no generic "connection failed"
New pure/testable helpers:
- _wifi_scan_parse(raw) — regex + sort + de-dup, returns list of dicts
- _format_apply_payload(ssid, password) — quoted serial payload,
rejects \r/\n, escapes embedded double-quotes
- _signal_bars(rssi) — block glyph for signal strength
Wiring:
- _ESP32_MIMICLAW_ITEMS gains a Settings submenu entry
- SUBMENUS["sub:mimiclaw:settings"] registered with WiFi item
- _mimiclaw_wifi dispatch token added
Verified:
- Pure-function unit tests pass (scan parse, payload formatting,
disconnect form, quote escaping, newline rejection)
- syntax clean on both files
Distillation across MicroPython/Marauder/Common footer is a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spec-mandated common footer distillation (docs/specs/2026-04-22).
Before (6 items, duplicated):
Install Bruce ← redundant: Reflash picker already has Bruce
USB Reset
Switch Firmware ← renamed below
Backup FW
Clear FW Cache ← rarely used, kept as dispatch token only
Re-detect
After (4 items, recovery first):
USB Reset ← most common fix when something is broken
Re-detect ← recovery — when the hub shows wrong firmware
Backup FW ← safety snapshot before reflash
Reflash ← renamed from Switch Firmware for clarity;
unchanged picker UI with all four firmwares
Dropped from the menu but handlers still exist (one-line re-add if
needed): "_esp32_install_watchdogs" and "_esp32_fw_cache_clear".
The full Reflash ▸ subfold design from the spec (with one-tap per
firmware + Clear Cache inside) requires refactoring the picker to
accept a preselect arg — deferred to a follow-up commit. This is the
minimal distillation that matches what the spec promised at the top
level: "4 top-level items, recovery first, destructive behind ⇄".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-firmware distillation from docs/specs/2026-04-22, applying the
cross-cutting patterns:
- Position 1 = primary action (what you open the submenu for)
- Position 2 = Serial Monitor (muscle memory across firmwares)
- Position 3 = Status (always "firmware info + chip rollup")
- Drop every duplicate; the common footer has the canonical verbs
MicroPython (8 → 6):
before: Status | Live Monitor | Serial Monitor | REPL | Flash Scripts
Reset | Log Entry | Chip Info
after: Live Monitor | Serial Monitor | Status | REPL
Flash Scripts | Log Entry
Live Monitor promoted to position 1 (the daily use case).
Status description widened to cover "chip info" so the dropped
Chip Info entry isn't missed.
Reset dropped — duplicates the common USB Reset.
Marauder (6 → 4):
before: Marauder | Serial Monitor | Scan APs | Device Info |
Settings | Reboot
after: Marauder | Serial Monitor | Status | Settings
"Device Info" renamed to Status for cross-firmware naming consistency.
Scan APs dropped — Marauder's own in-app menu already has it, so the
top-level shortcut was one extra way to do the same thing.
Reboot dropped — duplicates the common USB Reset.
MimiClaw (already distilled in 5345632: Chat | Serial | Status | Settings).
Total: 14 items removed across the three modes. Each now has 4–6
focused entries plus the shared 4-item footer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
framework.py treats VERSION as the last *shipped* version and a dev checkout displays it as `(VERSION + 1)-dev`. Commit 9583f11 ("release: prep v0.2.2") bumped VERSION ahead of cutting v0.2.2 — but v0.2.2 was never tagged, so VERSION drifted to 0.2.2 while the latest tag stayed at v0.2.1. Result: the dev TUI showed 0.2.3-dev instead of the expected 0.2.2-dev. Reverted VERSION and device/VERSION to 0.2.1 to restore the invariant. The v0.2.2 prep work in CHANGELOG.md stays — it documents what's queued for the next release. When `make release` cuts v0.2.2, the bump-patch step will advance VERSION to 0.2.2 atomically with the tag, exactly as the release flow expects. Verified: dev tree resolves to PKG_VERSION = '0.2.2-dev'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adapts the gamepad reader and trackball-scroll daemon to the new bb9900 ZMK keyboard. ZMK firmware emits ABXY as F21–F24 over the keyboard HID interface and exposes no joystick node, so we side-channel-read the keyboard's evdev node without grabbing it. trackball-scroll switches from KEY_SELECT to KEY_FRONT (Fn-alone) on the new device names. wait_for_input migrated to read EVDEV_SIZE chunks and unpack with EVDEV_FMT, returning only on key-down for codes in _KEY_TO_GP — fixes a runtime NameError on the old JS_SIZE/JS_FMT constants that the initial port missed. Verified: pyflakes clean, 1142 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stock uConsoles use the built-in brcmfmac (wlan0) as the only radio, but it's possible to route antennas to a USB adapter (e.g. AC12000 → wlan1). Replace the wlan0 hardcode with a sourced config-file override: default stays wlan0, devices that need a different radio drop a single WIFI_IFACE= line into /etc/uconsole/wifi.conf. Also: harden wifi-fallback.sh against being run as an NM dispatcher, where HOME is unset and `set -u` made the $HOME/.config STATE_DIR lookup crash. Falls back to UID 1000's home. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Working-tree scrub of personal data that had accumulated across code, tests, fixtures, docs, and the .deb staging dir: - wifi.sh / wifi-fallback.sh: hardcoded NM connection names (HOME_CON / OFFICE_CON / IPHONE_CON) now sourced from /etc/uconsole/wifi.conf via WIFI_HOME_CON / WIFI_OFFICE_CON / WIFI_IPHONE_CON. Empty defaults; the home/office/iphone shortcuts print a "not configured" error if the corresponding var is unset. fallback dispatcher's iPhone path is a no-op when WIFI_IPHONE_CON is empty. - monitor.py: hardcoded "WiFi: Big Parma - 2.4GHz" sample replaced with "WiFi: -" placeholder (the line was never wired to live data). - wifi_radio.py / iw_dev.txt fixture / parser test: anonymized SSIDs and MACs. - wardrive demo + preview ESSID pools: replaced a real phone-hotspot SSID with a generic placeholder. - docs (bookworm-migration, two AIO-v2 specs/plans, mimiclaw spec, TUI audit): replaced real SSIDs / LAN IPs / a literal WPA password with <placeholder> tokens. - device/scripts/ssh/id_ed25519.pub: removed. Was already excluded from the .deb by packaging/build-deb.sh, but had no business sitting in the canonical tree. History rewrite is a separate decision — this commit only stops new clones from picking up the data. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Local CLI agent that talks to the on-device ollama daemon (default qwen2.5:7b). Stdlib-only Python — no extra deps. Tools: run_bash (confirms on mutating commands like sudo/rm/mv/dd/chmod/systemctl/ apt/git-push/redirects), read_file, write_file, list_dir. Modes: - one-shot: uconsole-ai "query" - REPL: uconsole-ai (with /reset, /model X, /exit) Flags: -y auto-confirm, -q hide tool traces, --full-context inline CLAUDE.md, --list-models. The shell shim auto-detects whether to run from the dev tree or /opt/uconsole/lib/. Tested on CM5 Lite 16GB (no GPU) — qwen2.5:7b is the working default; ~2 tok/s gen, 60-195s per agent turn. Use case: "send query, walk away," not interactive chat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-event motion-passthrough/wheel-emit/grab-OK lines were flooding journalctl during normal operation, drowning out everything else when reviewing system logs. Gate them behind a TRACKBALL_DEBUG env var so the daemon stays quiet by default; failures (grab=FAIL, ungrab=FAIL) remain unconditional since those still want surfacing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CI install-test "no personal data in package" check (Dockerfile.test:114) caught two leaks in the initial uconsole-ai commit: - a hardcoded `/home/mikevitelli/CLAUDE.md` path in the resolver list (Path.home() / "CLAUDE.md" already covers the right case) - "User: mikevitelli." in the fallback system-prompt string Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pick the lowest-metric default route to label the NET panel — shows "Wired <ip>" when ethernet is plugged in instead of always falling back to the wifi SSID. Also reads signal from the active wifi interface (was hardcoded to wlan0, which is unmanaged on AC12000). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds CONFIG → Monitor Refresh picker (250ms / 500ms / 1s / 2s / 3s / 5s), persisted as monitor_refresh_ms in console.json. run_live_monitor reads the saved value (default 1000) and the footer label reflects it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds --chown=root:root to the rsync calls in `make install` so files under /opt/uconsole/ are owned by root regardless of who ran the command. Previously they inherited the invoking user's ownership, which is wrong for a system-installed package. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…isable State directory was under $XDG_CONFIG_HOME/wifi-fallback (per-user $HOME), but the script runs as a NetworkManager dispatcher under root with no useful HOME. Move state to /var/lib/wifi-fallback where it belongs for a system daemon and drop the HOME-fallback gymnastics. Also gate enable/disable subcommands behind a sudo re-exec so users running them interactively don't silently fail to create /var/lib entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Native TUI screen for the MT7921 (AC1200) 2x2 array. Chain A is the top trace, B the bottom, and the filled braille band between them is colored by the |A-B| delta — a marginal connector shows as the band fattening and going red. Reuses tui_lib.BrailleCanvas + framework input/colors, same class as GPS Globe / FM Radio. - EMA-smooths each chain before drawing (raw per-poll RSSI speckles) - auto-ranged Y (min_span 10, pad 0.15) so wiggles show even at -32 dBm - widened balance thresholds: <=6 balanced / <=12 drifting / >12 check - fixed fast 250ms refresh, one BLE spinner, no flashing/ambient - self-contained module; framework import-isolation keeps a fault from affecting the rest of the menu Registered tui.antenna in FEATURE_MODULES + sub:wifi menu entry.
Replace hardcoded "UCONSOLE" in the scanline header with socket.gethostname().upper(), cached once before the render loop. The TUI is the same package installed on all uConsole-class devices via apt.uconsole.cloud — the header now reads "◈ UCONSOLE ──", "◈ SERVER ──", "◈ PI-TOP ──" etc. per host instead of a misleading constant. Falls back to UCONSOLE if gethostname() returns empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The .claude/commands/ slash-command prompts and .vscode/settings.json (which only contained `claudeCodeChat.permissions.yoloMode: true`) are local developer ergonomics, not part of the public package. Strip them from the source tree. Harden .gitignore so future AI-tool scratch dirs (.cursor, .copilot, .continue, .aider*, AGENTS.md, GEMINI.md) and credential-flavored filenames (*.key, id_*, secrets/, .config/op/) can't sneak in via `git add .`. No tracked files match the new patterns today — these are defensive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove 33 files that were operator's personal /etc and user-session
state, not part of the package payload:
- device/scripts/config/gh/{repos.txt,config.yml} — personal GitHub
repo list (incl. private repos) and gh CLI prefs
- device/scripts/config/pulse/cookie — PulseAudio auth cookie
- device/scripts/config/systemd-user/{claude-relay-agent,evolution-*,
goa-daemon,gvfs-*,webdash}.service — operator's GNOME/Evolution
user-session services and a webdash.service with hardcoded
/home/mikevitelli/scripts/webdash.py and a default WEBDASH_PASS.
The package installs packaging/systemd/uconsole-webdash.service,
which is the canonical version. trackball-scroll.service stays.
- device/scripts/config/{chromium,dconf,gtk-3.0,gtk-4.0}/ — desktop
state dumps (themes, prefs, dconf-dump.ini with NM eap UUIDs)
- device/scripts/config/{mimeapps.list,themes.txt}
- device/scripts/system/etc/{fstab,hostname,hosts,crontab.user,
sshd_config,keyboard,locale,timezone} — fstab pinned a specific
PARTUUID, crontab.user had hardcoded /home/mikevitelli/ paths
- device/scripts/system/etc/sudoers.d/* — operator's env-keep tweaks
- device/scripts/system/alsa/asound.state — operator's audio mixer
state; restore.sh reads from the private backup repo ($REPO_DIR),
not from this tree, so removing here is safe
build-deb.sh already scrubs scripts/system/etc and scripts/config
from the .deb (lines 63, 65), so this commit changes nothing about
what end users install — it only removes the source-tree exposure
on GitHub.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ifacts
- Consolidate three docs trees into two:
- device/docs/troubleshooting.md → docs/TROUBLESHOOTING.md
- device/docs/README.md (was just a cloud-docs redirect) deleted
- docs/{audits,plans,specs} → docs/internal/{audits,plans,specs}
separates public-facing docs (ARCHITECTURE, API, FEATURES, etc.)
from internal planning trail
- device/webdash/docs/ kept as-is (served by the webdash wiki panel)
- Fix device/scripts/util/lib.sh broken symlink: was pointing at
/opt/uconsole/lib/lib.sh (broken in any fresh clone before
`make install`). Now a relative symlink to ../../lib/lib.sh
so the dev tree works straight from git.
- Remove device/lib/tui/adsb_basemap_global.json.bak (482KB dead
backup file, unreferenced; only adsb_basemap_global.json is
read by adsb.py:128).
- packaging/copyright Upstream-Contact: mike@chopcheese.nyc →
mike@uconsole.cloud to match packaging/control's Maintainer
field and avoid surfacing the personal domain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
packaging/copyright had mike@chopcheese.nyc (personal/business domain) while packaging/control's Maintainer field already used mike@uconsole.cloud. Match them so apt-cache pages and dpkg-deb -I output show consistent contact info. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README rewrite (211 → ~290 lines): - Tagline reframed from "remote monitoring" to "complete software stack — TUI, web dashboard, and optional cloud telemetry" to match what's actually shipping. The device side has outgrown the cloud half by a wide margin. - TUI section corrected: 23 feature modules across 9 categories (was "9 categories, 64 native handlers"). Highlights per category. - New "Subsystem highlights" section walks the marquee shipping pieces one paragraph each: offline AI agent, MimiClaw, Marauder + wardrive, Meshtastic (with the SX1262 init fixes that AIO v1 boards needed), ADS-B (readsb migration), antenna array monitor, battery/power hygiene, WiFi suite. - "Hardware target" section explicitly names CM4 and CM5, AIO v1/v2, MT7921 AC1200, SX1262 LoRa, and documents the PCIe Gen 2 derate. - Security table picks up 4 new rows: push rate-limit/shape-check, device-pushed wifi.ip validation, shell hardening (eval/source replacement), CI SHA pinning. - "Related repos" section calls out the mikevitelli/uconsole private backup repo so newcomers don't confuse the two. - License section added. CHANGELOG.md: rename "v0.2.2 (unreleased)" → "v0.3.0 (2026-05-26)", add a sentence covering the items that landed since the heading was first drafted (uconsole-ai, antenna monitor, hostname-aware live monitor, OSS hygiene pass). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README + CONTRIBUTING.md: "100+" / "47" management scripts → "50+" (actual count under device/scripts/ is 51 — pick a number that holds in both docs) - CHANGELOG.md: add "## (unreleased)" section above v0.3.0 so v0.4 work has a clear landing spot. Drop the stale "What's next (v0.1.8+)" stub at line 233 — all four items shipped or no longer relevant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reference-style file (links to contributor-covenant.org rather than inlining the full 2.1 text) — standard practice, recognized by GitHub's community-profile detector. Brings community profile from 85% to 100% and removes the "Add CODE_OF_CONDUCT.md" banner GitHub shows on the Insights tab. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the v0.3.0 prep, a 6-track audit (OSS front door, docs completeness, code quality, security, roadmap coherence, ops & observability) surfaced ~100 findings. The pre-publish quick fixes landed in v0.3.0; the rest is captured here so it doesn't drift. Structured into: - v0.3.1: 11 close-the-gap items (workspace-monitor decision, default webdash password, session invalidation, observability quick wins, OSS-front-door polish) - v0.4: 17 bigger lifts (marauder.py split, first-5-min guide, HW compatibility matrix, runbook, dependabot, etc.) - Conditional: multi-device backbone, suspend-to-RAM, workspace gamepad isolation enhancement Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Non-major bump that closes ~19 stacked advisories on the prior version: - CVSS 8.6 SSRF (GHSA-c4j6-fc7j-m34r) - CVSS 8.1 Middleware bypass via dynamic route params (GHSA-492v-c6pp-mqqv) - CVSS 7.5 DoS × 3 (GHSA-q4gf-8mx6-v5v3, GHSA-8h8q-6873-q5fj, GHSA-26hh-7cqf-hhc6) - Plus XSS (GHSA-gx5p-jg67-6x7h) and other moderate findings Verified: `npm run lint` clean, all 212 vitest tests pass. Remaining npm-audit findings (6 moderate, 4 high) are transitive dev-time deps (vite, picomatch, flatted) — not in production bundles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This reverts commit c76152c.
auto-merge was automatically disabled
May 26, 2026 07:12
Rebase failed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated release PR from /publish.
134 commits since v0.2.1: TUI framework rewrite, offline AI agent (
uconsole-ai), Meshtastic mesh map, ADS-B feeder migration (dump1090 → readsb + viewadsb), AIO v2 rail dashboard, MimiClaw ESP32 chat portal, ESP32 Marauder hub + wardrive beta, antenna array braille ribbon monitor (MT7921 chains), hostname-aware Live Monitor header, configurable refresh rate, low-battery threshold retune for Samsung INR18650-35E cells, install ownership fix, postinst path cleanup, OSS-hygiene pass (operator-config snapshots stripped from source — .deb payload unchanged), CODE_OF_CONDUCT, refreshed README, v0.3.1+v0.4 roadmap, Next.js 16.1.6 → 16.2.6 (closes 19 advisories).See CHANGELOG.md for the full breakdown.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com