Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2de5ee0
dotbot/console: add the unified web console app
geonnave Jul 9, 2026
c4bf1cd
dotbot/console: add dev harness (fake swarmit server + demo fleet)
geonnave Jul 9, 2026
effbd47
dotbot/console: rename to console-web (a console could also be a TUI)
geonnave Jul 9, 2026
bccfc86
dotbot/console-web: add list/grid views, minimap pan, toasts
geonnave Jul 9, 2026
3cf5c0d
dotbot/console-web: fidelity pass to match design v1 semantics and gl…
geonnave Jul 9, 2026
f54066a
dotbot/console-web: drop wheel zoom (not in the design; zoom buttons …
geonnave Jul 9, 2026
fb4dabd
dotbot/console-web: add testbed rail with missions panel
geonnave Jul 9, 2026
222cd95
dotbot/console-web: persistent planned missions, square minimap, tall…
geonnave Jul 9, 2026
599627c
dotbot/console-web: upgrade fake swarmit to a stateful write-capable …
geonnave Jul 9, 2026
418c769
dotbot/console-web: orchestration write path (flash dialog, SSE progr…
geonnave Jul 9, 2026
3cb131f
dotbot/console-web: track canvas size live so the arena keeps its mar…
geonnave Jul 9, 2026
245c80f
dotbot/console-web: theme dev param + puppeteer screenshot helper
geonnave Jul 9, 2026
330a08a
dotbot/console-web: manual test guide
geonnave Jul 9, 2026
e47978d
dotbot/console-web: file-manager selection in list/grid (shift range,…
geonnave Jul 9, 2026
96df0e8
dotbot/console-web: fix heading pointer and pad steering sense vs con…
geonnave Jul 9, 2026
e951409
dotbot/console-web: background click deselects; click-again on sole i…
geonnave Jul 9, 2026
d1cf4b4
dotbot/console-web: screen-relative pad steering (drag where the bot …
geonnave Jul 9, 2026
0a8667d
dotbot/config: add [run.controller] swarmit_url and --swarmit-url
geonnave Jul 10, 2026
a4e2203
dotbot/server: serve the console at /console and proxy /swarmit/*
geonnave Jul 10, 2026
d6c9c83
dotbot/console-web: relative asset base and live host label
geonnave Jul 10, 2026
a884be8
dotbot/console-web: add vitest suite, eslint, and typecheck script
geonnave Jul 10, 2026
eced9e9
.github/workflows: check console-web (lint, typecheck, tests, build)
geonnave Jul 10, 2026
0ba08fa
tox: lint console-web in the web env
geonnave Jul 10, 2026
efb0122
pyproject: ship the console-web app in sdist and wheel
geonnave Jul 10, 2026
7b39dfc
dotbot/server: black reformat
geonnave Jul 10, 2026
37dfd4b
dotbot/console-web: black reformat
geonnave Jul 10, 2026
7912b45
fix(console-web): interpolate bot positions with adaptive duration
RasdaCorentin Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,37 @@ jobs:
name: frontend
path: ./dotbot/frontend/build

console:
name: check console
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up nodejs
uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install
working-directory: ./dotbot/console-web
- run: npm run lint
working-directory: ./dotbot/console-web
- run: npm run typecheck
working-directory: ./dotbot/console-web
- run: npm run test
working-directory: ./dotbot/console-web
- run: npm run build
working-directory: ./dotbot/console-web
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
verbose: true
- name: Upload console build
uses: actions/upload-artifact@v4
with:
name: console
path: ./dotbot/console-web/dist

package:
needs: [test, doc, frontend, control_loop]
needs: [test, doc, frontend, console, control_loop]
name: build source package
runs-on: ${{ matrix.os }}
strategy:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*.log
*.egg-info/
.coverage
coverage/
.tox/
dist/
node_modules/
Expand Down
1 change: 1 addition & 0 deletions dotbot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
MQTT_PORT_DEFAULT = 1883
MAP_SIZE_DEFAULT = "2000x2000" # in mm unit
SIMULATOR_INIT_STATE_DEFAULT = "simulator_init_state.toml"
SWARMIT_URL_DEFAULT = "http://localhost:8001" # swarmit server default port


def pydotbot_version() -> str:
Expand Down
24 changes: 17 additions & 7 deletions dotbot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class ControllerSection(_Strict):
headless: bool | None = None
gw_address: str | None = None
simulator_init_state: str | None = None
swarmit_url: str | None = None


class GatewaySection(_Strict):
Expand Down Expand Up @@ -292,11 +293,13 @@ def _env_candidates(section: str | None, key: str) -> tuple[str, ...]:
"""Env-var names to check, in priority order (Cargo's mechanical mapping).

Sectioned key -> `DOTBOT_<SECTION>_<KEY>`, then the shared `DOTBOT_<KEY>`
alias. Top-level key -> just `DOTBOT_<KEY>`.
alias. Top-level key -> just `DOTBOT_<KEY>`. A nested section like
`run.controller` flattens its dots: `DOTBOT_RUN_CONTROLLER_<KEY>`.
"""
key_part = key.upper().replace("-", "_")
if section:
return (f"DOTBOT_{section.upper()}_{key_part}", f"DOTBOT_{key_part}")
section_part = section.upper().replace(".", "_")
return (f"DOTBOT_{section_part}_{key_part}", f"DOTBOT_{key_part}")
return (f"DOTBOT_{key_part}",)


Expand All @@ -318,11 +321,17 @@ def _file_value(
key: str,
deployment: Deployment | None,
) -> Any:
"""The value this key has in the file layer: section > deployment > top-level."""
"""The value this key has in the file layer: section > deployment > top-level.

`section` may be nested (dot-separated, e.g. `run.controller`); each part
is walked with getattr.
"""
if config is None:
return None
if section is not None:
section_obj = getattr(config, section, None)
section_obj: Any = config
for part in section.split("."):
section_obj = getattr(section_obj, part, None)
value = getattr(section_obj, key, None)
if value is not None:
return value
Expand All @@ -348,9 +357,10 @@ def resolve(
`flag` > env (`DOTBOT_<SECTION>_<KEY>`, then shared `DOTBOT_<KEY>`) >
file (section > deployment > top-level) > `default`.

`section` is one of `SECTIONS` for a per-namespace key, or `None` for a
top-level shared key (e.g. `conn`, `swarm_id`). Env values are coerced to
the type of `default`.
`section` is one of `SECTIONS` for a per-namespace key, a dotted path for
a nested table (e.g. `run.controller`), or `None` for a top-level shared
key (e.g. `conn`, `swarm_id`). Env values are coerced to the type of
`default`.
"""
if flag is not None:
return flag
Expand Down
81 changes: 81 additions & 0 deletions dotbot/console-web/dev/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Console manual test guide

Start the stack (three terminals, from the workspace root, venv active):

```bash
PYTHONPATH=repos/wt-PyDotBot-unified-web-ui dotbot run simulator --headless \
--simulator-init-state repos/wt-PyDotBot-unified-web-ui/dotbot/console-web/dev/simulator_init_state.toml

python repos/wt-PyDotBot-unified-web-ui/dotbot/console-web/dev/fake_swarmit_server.py

npm --prefix repos/wt-PyDotBot-unified-web-ui/dotbot/console-web run start
```

Open http://localhost:5173. Handy URL params: `?sel=1111` (preselect),
`?view=list|grid`, `?rail=testbed|missions`, `?theme=light`.

If the title bar says OFFLINE: the vite proxy targets do not match where the
controller runs. `curl localhost:5173/controller/dotbots` must return bots.

## Checklist

Map + selection
- [ ] Title bar: LIVE (green pulse), bot count; Dark|Light toggle flips theme
- [ ] Bots move smoothly; heading pointer at the circle edge; battery bar above
- [ ] Plain drag pans (clamped); plain click clears selection
- [ ] Shift-drag marquee selects; shift-click toggles one bot
- [ ] Click bot: red rectangle + id chip; hover another bot: chip appears
- [ ] Zoom +/-/recenter; arena keeps margins when rail opens or window resizes
- [ ] Layers panel: Battery Bars / Waypoints / DotBots / Real-scale / Trails toggle live

Footer (bottom strip)
- [ ] Nothing selected: N/1000 + per-state rollup; click a state row selects those bots
- [ ] One bot: LED thumb, short id, device, Drivable pill, state, battery, "x, y mm"
- [ ] Ghost bot (0001/0002): "Not drivable" pill, dock grayed, warning hint
- [ ] Multi: "N selected", per-state mini-rollup, dock drives the group
- [ ] Minimap: square (arena aspect), dots colored by state, drag moves the map view

Control dock
- [ ] Joystick: drag = bot drives, release = stops; knob shows LED color + live heading
- [ ] LED button -> swatch grid -> bot circle + map dot recolor (toast confirms)
- [ ] Alt-click map queues waypoints (dashed diamonds); popover lists "x, y mm" with per-item remove
- [ ] Go sends: bot navigates, diamonds go solid, button morphs to "Stop nav"
- [ ] Deselect and reselect other bots: the Planned mission survives (see rail Missions)

Testbed rail
- [ ] Icon strip -> panel; Testbed tab: Target label follows selection
- [ ] Flash... dialog: pick image, Flash N device(s); bots blink amber, queue tab
shows per-device % bars, fleet % bar in Testbed tab, footer shows chunk progress
- [ ] After flash: bots in Bootloader (not drivable) -> Start returns them to Running
- [ ] Stop: Stopping -> Bootloader; Reset: Resetting -> Bootloader
- [ ] Console tab: log lines stream (flash/start/stop events), Clear works
- [ ] Missions tab: Planned (Go / discard) and Active (interrupt) rows; click row
reselects its bots; arrival adds a "Recently completed" line

List / Grid
- [ ] Search by id, state filter, column sort; checkbox multi-select + select-all
- [ ] Selection carries across Map/List/Grid and drives the same footer dock

## Scripted actions (against the running stack)

```bash
# color a bot's LED
curl -X PUT localhost:8000/controller/dotbots/badcafe111111111/0/rgb_led \
-H 'Content-Type: application/json' -d '{"red":34,"green":197,"blue":94}'

# send a waypoint mission (bot navigates on its own)
curl -X PUT localhost:8000/controller/dotbots/badcafe111111111/0/waypoints \
-H 'Content-Type: application/json' \
-d '{"threshold":60,"waypoints":[{"x":400,"y":1600},{"x":1600,"y":400}]}'

# flash two bots (watch rail queue + console)
curl -N -X POST localhost:8001/flash/stream -H 'Content-Type: application/json' \
-d '{"firmware_b64":"ZmFrZQ==","devices":["badcafe111111111","deadbeef22222222"]}'

# stop / start / reset a subset (or omit devices = whole fleet)
curl -X POST localhost:8001/stop -H 'Content-Type: application/json' \
-d '{"devices":["badcafe111111111"]}'

# screenshot for the Claude Design re-seed loop
node dotbot/console-web/dev/screenshot.mjs "http://127.0.0.1:5173/?sel=1111" out.png
```
Loading
Loading