dotbot/console-web: unified web console for control + orchestration - #279
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #279 +/- ##
============================================
- Coverage 83.55% 66.06% -17.49%
============================================
Files 121 142 +21
Lines 11266 15171 +3905
Branches 570 691 +121
============================================
+ Hits 9413 10023 +610
- Misses 1850 5135 +3285
- Partials 3 13 +10
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
…yphs Adopts the design's north-up y convention (screen top = arena max-y), which deliberately diverges from dotbot/frontend's y-down map - keep that in mind when comparing positions across the two UIs. AI-assisted: Claude Fable 5
…only) AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
…er footer AI-assisted: Claude Fable 5
…simulator AI-assisted: Claude Fable 5
…ess, console) AI-assisted: Claude Fable 5
…gins AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
… cmd toggle) AI-assisted: Claude Fable 5
…troller conventions The controller reports direction as 0 = north (+y), positive counter- clockwise, so the north-up display must negate it; the pad's steering sign is verified against the simulator (pad-right = clockwise arc) but the classic frontend used the opposite sign on real hardware - validate on a real DotBot before trusting teleop in the field. AI-assisted: Claude Fable 5
…tem deselects AI-assisted: Claude Fable 5
…should go) The pad closes the heading loop itself at 10 Hz (P on heading error, alignment-scaled throttle with a floor that keeps the motion-derived heading observable). Replaces body-relative throttle/yaw teleop, which felt erratic; verified convergent against the simulator. AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
AI-assisted: Claude Fable 5
The bot marker used a fixed CSS transition (left/top .2s linear). Position updates arrive at whatever rate the source reports them (~20Hz in simulation, sparser and irregular from real LH2 hardware), so the fixed 200ms duration is either too short relative to the gap between updates (the bot glides for 200ms then holds still until the next update, producing visible stutter) or too long (a fast update interrupts the transition already in flight). The old frontend has no transition at all and snaps instantly, which is the same defect in its more visible form. Add useSmoothPositions: a small per-bot animation state driven by requestAnimationFrame, using the previously observed update interval as the next animation's duration (clamped to [60ms, 600ms]) instead of a constant. A jump larger than 35% of the arena diagonal in one update is treated as a teleport and rendered instantly rather than animated across the whole arena. MapView now reads bot render position from this hook instead of the raw prop, and the CSS transition is removed (redundant with per-frame interpolation). Verified: vitest (7 new cases covering first-sight snap, in-flight retarget, duration clamping, teleport detection), tsc build, eslint, and a manual pass against the running simulator + vite dev stack. AI-assisted: Claude Sonnet 5
geonnave
force-pushed
the
unified-web-ui
branch
from
August 19, 2026 12:01
7912b45 to
5948273
Compare
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
A curated view rather than the settings object: the same settings carry mqtt_username and mqtt_password, and this route answers any browser that can reach the controller. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…ng them Needs a swarmit serving reset_cause, fault_name, image_state_name, image_result_name and info.lh2_summary; against an older one those fields read as unknown rather than being recomputed here, which is what kept the two vocabularies drifting apart. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
…slot AI-assisted: Claude Opus 5
Reset could not work from here: swarmit's /reset takes a locations map rather than a device list, so the call 422s, and the netcore's handler stores the position but never triggers the SoC reset - the line is commented out. The action type is narrowed so it cannot come back by accident. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
The simulated bot's address is uppercase but the rx path compared it to hex()[2:], which is lowercase and unpadded, so the gate was never true and every move and waypoint frame was dropped: commands returned 200 and the bot did not move, with nothing logged. The uplink index lookup missed the same way and fell back to bot 0, silently routing every frame through the wrong network mode. AI-assisted: Claude Opus 5
swarmitAction resolved on any status, so a 502 from an absent swarmit server raised a "Start sent" toast; the map drew swarmit's (0, 0) no-fix sentinel as an arena corner, which puts an uncalibrated fleet in one pile that reads as real; the file input kept its value, so re-picking a rebuilt image fired no change event and flashed the bytes captured the first time; and a stale queue-clear timer could leave a job without its address, which throws where nothing catches it. AI-assisted: Claude Opus 5
The flag is hand-typed, so it is normalised where it is stored rather than at the REST boundary, which would be a compat shim. An address that matches nothing now warns with the address instead of logging at info and exiting silently. AI-assisted: Claude Opus 5
AI-assisted: Claude Opus 5
The classic frontend stays mounted at /PyDotBot: it is the only place the qrkey demo, the REST demo and the SailBot views exist, so this changes which UI opens rather than retiring one. The path is chosen from what is actually built, so a source checkout with no console still opens the classic UI instead of a 404. AI-assisted: Claude Opus 5
Two controller tests drive the full run loop with headless defaulting to false, so every local run put two tabs on the developer's screen. They now set it, and an autouse fixture neutralises webbrowser.open for the whole suite so a future test cannot reintroduce this. AI-assisted: Claude Opus 5
Breaking: the REST/WS API was served on 0.0.0.0 with no authentication, and /swarmit/* now forwards to the swarmit server, so a wide bind also republished an otherwise-local swarmit at the controller's reachability. --controller-http-host restores the old behaviour and warns when it is used. AI-assisted: Claude Opus 5
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.
Adds
dotbot/console-web, a map-first web console that merges the PyDotBot control plane and swarmit orchestration into one UI: Map/List/Grid views with shared selection, a footer control dock (drive pad, LED, waypoint missions), a testbed rail (firmware flashing with per-device SSE progress, start/stop, a streaming event console, missions), and an inspector reporting what each bot is running and why it last rebooted. React 18 + Vite + TS, no UI framework, every color through design tokens, dark and light themes. Ported from a Claude Design prototype.The controller serves the built console at
/console, side by side with the classic/PyDotBotfrontend, and proxies/swarmit/*to the swarmit server through a streaming httpx route so SSE stays live and production is same-origin exactly like the vite dev proxy. The swarmit endpoint is configurable:[run.controller] swarmit_urlin dotbot.toml,--swarmit-url, orDOTBOT_SWARMIT_URL; the defaulthttp://localhost:8001matchesswarmit serve. The config resolver learned dotted nested sections for this.One change reaches outside the console
Device addresses are now rendered uppercase everywhere, through a single
dotbot.addr_to_hex()helper that is the only place a 64-bit address becomes a string. This is not cosmetic. The address is the join key between the two data planes; swarmit spells it uppercase andbinascii.hexlifyreturns lowercase, so at the bench one real robot arrived under two keys and drew as two bots, neither record complete.DOTBOT_ADDRESS_DEFAULTandGATEWAY_ADDRESS_DEFAULTwere already written uppercase, so this makes the codebase agree with itself - but anything comparing addresses case-sensitively against controller output will see the difference.Also new outside the console:
GET /controller/connection, reporting adapter, connection URI, swarm id and gateway address so the console can show which broker and which swarm it is driving. The response model is curated rather than a dump of settings, and a test asserts the MQTT credentials never appear in it.Things a reviewer should know
dotbot swarm statusabout the same robot. Those fields arrive with swarmit/testbed: serve computed status strings and a per-device battery reading swarmit#160, which should land first.WatchdogTimeout. Without the split, a normal fleet shows red everywhere./resetis dead end to end (wrong payload shape, and the firmware trigger is commented out). A button that does nothing is worse than an absent one./flash/streamprogress reaches only the requesting client; multi-client progress belongs in/eventsor/status(to file upstream).codecov.ymlnow gates on the Python project coverage and reports the two web apps informationally, so a frontend-heavy change does not fail against Python's project target.doc/cli/run.mdfor--swarmit-url,doc/reference/configuration.md, a console section) is deliberately not in the diff yet and lands before merge, after review of the functional change.Validation
End-to-end against
dotbot run simulatorplus the fake swarmit server underdotbot/console-web/dev/(all views and themes, waypoint missions, a full flash and start cycle, and the production build served from the controller). Then a bench session against real hardware - one DotBot v3 on a live MQTT broker withswarmit servealongside - which is what found the address-casing join bug, the battery scale, and the fact that the closed-loop drive pad was unusable on a real robot. Automated: vitest for the pure logic (state merge, mission tail-grouping, SSE parsing, inspector formatting), pytest for config resolution, the proxy and the new route, aconsoleCI job mirroring the frontend one, and sdist/wheel builds inspected to confirm the built console ships.The dev harness implements the real swarmit HTTP contract, so pointing the console at a real swarmit server is a URL change.
Internal context for DotBots org members: rendered tracker at https://claude.ai/code/artifact/11e2969b-9d34-44a7-80c2-b63215b7d56f, source at https://github.com/DotBots/dotbot-workspace/blob/main/plans/unified-web-ui/implementation.html, board item DBK-127. Both are private to the org; everything a reviewer needs is above.