Skip to content

Commit 1344b96

Browse files
committed
feat(map): add map exploration script and local map editor plan
- Add explore_parser.py for inspecting MapData structure - Add local-map-editor.md implementation plan - Update CLI to use v4 login API (request_code_v4, code_login_v4) - Add test files for CLI and Qrevo commands - Update .gitignore for dev artifacts Entire-Checkpoint: 504b88d296f6
1 parent 794adfa commit 1344b96

9 files changed

Lines changed: 1236 additions & 2 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ gha-creds-*.json
2323
# Claude Code / OMC agent state
2424
.claude/
2525
.omc/
26+
27+
# Development artifacts
28+
.entire/
29+
.sisyphus/
30+
explore_map.png

docs/plans/local-map-editor.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Local Map Editor (Optimistic UI)
2+
3+
## TL;DR
4+
5+
> **Quick Summary**: Build a conversational, programmatic map editing workflow. This tool will allow users to describe edits to a Roborock map via natural language. The system will maintain a "Virtual State" locally, render modified PNGs for user approval, and then translate approved changes into physical `RoborockCommand` RPC calls to the robot.
6+
>
7+
> **Deliverables**:
8+
> - A Geometric Math Engine for parsing `MapData` into calculable structures (polygons, lines, points).
9+
> - A Local State Machine supporting "Edit Objects" (Virtual Walls, No-Go Zones, Room Splits/Merges) with Undo/Revert.
10+
> - An API Translation Layer mapping approved Virtual State changes to `device.send_command(...)`.
11+
> - A Verification Loop to ensure the robot's cloud map matches the intended local state.
12+
>
13+
> **Estimated Effort**: High (requires significant mathematical/geometric implementation).
14+
15+
---
16+
17+
## Architectural Workflow
18+
19+
The system is designed around an **Optimistic UI Pattern**, split into three distinct phases:
20+
21+
### Phase 1: Local Replication & The "Virtual State"
22+
1. **Pull Data**: `HomeTrait.discover_home()` fetches the raw binary map via `GET_MAP_V1`.
23+
2. **Initialize Local State**: The `MapParser` (from `vacuum-map-parser-roborock`) reads the binary into a local `MapData` object.
24+
3. **Geometric Engine**: We convert Roborock's internal coordinate system (usually mm relative to a charger or origin point) into manipulatable Python shapes (using `shapely` or standard geometry math).
25+
* _Challenge_: Mapping the coordinate scale from the binary array directly to bounding boxes (`[min_x, max_x, min_y, max_y]`).
26+
27+
### Phase 2: The Conversational Edit Loop
28+
When the user requests an edit (e.g., "Add a virtual wall across the hallway"):
29+
1. **Mutation**: Instead of modifying the robot immediately, the system generates an "Edit Object" (e.g., `VirtualWall(x1, y1, x2, y2)`).
30+
2. **Apply Local Edit**: The Edit Object is appended to a local stack and applied to our Virtual State `MapData`.
31+
3. **Interactive Preview**: The CLI generates a temporary PNG (e.g., `temp_preview.png`) with the proposed edits drawn in red. The user is prompted to check this file before proceeding.
32+
4. **Approval/Undo**: The user reviews the PNG. If incorrect, we pop the Edit Object off the stack (Undo). If approved, we proceed to Phase 3.
33+
34+
### Phase 3: Execution & Verification (Two-Stage Sync)
35+
Because structural edits (like `SPLIT_ROOM`) destroy old Room IDs and create new ones, applying additive edits (like `SET_VIRTUAL_WALL`) immediately afterward could target an invalid Room ID or corrupt the map. Therefore, the Translation Layer must execute a **Two-Stage Sync**:
36+
37+
1. **Stage 1 (Structural Sync)**: Translate and send all room split, merge, and rename commands.
38+
2. **Intermediate Repopulate**: Pause and poll `GET_MAP_V1` until the firmware reflects the structural changes. Update the Local State Machine with the new, valid Room IDs.
39+
3. **Stage 2 (Additive Sync)**: Re-map any remaining "Edit Objects" (Virtual Walls, No-Go Zones, Carpet Areas) to the newly generated Room IDs and send those commands to the robot.
40+
4. **Final Verification Polling**: Firmware processing is asynchronous. We must implement a polling loop (e.g., 3 retries over 10 seconds) calling `GET_MAP_V1` to verify that the robot's final state matches our Local State. Catch firmware rejections (e.g., virtual wall intersecting the dock).
41+
42+
---
43+
44+
## Critical Considerations & Risk Mitigation
45+
46+
### 1. Partial Failures & Transactional Integrity
47+
* **Risk**: If a batch of 5 edits is sent and the 4th fails, the first 3 are permanently applied with no automatic rollback.
48+
* **Mitigation**:
49+
- **Physical Rollback**: The Local State Machine must track "inverse" commands for every action.
50+
- **Pre-Sync Snapshot**: Investigate `manual_bak_map` and `recover_map` commands to create a restorable checkpoint before applying batches.
51+
52+
### 2. Map Switch Safety (State Blocking)
53+
* **Risk**: `HomeTrait.discover_home()` physically switches maps, which takes time and can fail if the robot is busy cleaning.
54+
* **Mitigation**: Prioritize the `DeviceCache`. If map data is already cached, perform all "Optimistic" edits against the cache. Only force a physical map switch/refresh if the user explicitly requests a "Live Sync" and the robot is idle.
55+
56+
### 3. Parser Dependencies (Black Box Extraction)
57+
* **Risk**: `vacuum-map-parser-roborock` is optimized for image rendering and may obscure or discard the raw binary/mathematical coordinates required for a Geometric Engine.
58+
* **Mitigation**: Exploration confirms the parser exposes `map_data.image.dimensions` (offset/scale) and raw `[x,y]` coordinates (in mm) for all rooms/walls. The raw math is fully accessible; no custom binary decoder is needed.
59+
60+
### 4. Protocol Fragmentation (V1 vs. A01/B01)
61+
* **Risk**: Payload structures for map editing may differ significantly between protocol versions (e.g., older S5 vs. newer Qrevo Curv).
62+
* **Mitigation**: Implement a polymorphic Translation Layer that selects the correct payload schema based on the device's protocol version and capabilities.
63+
64+
### 5. Map Desynchronization (State Drift)
65+
* **Risk**: If the robot rotates or overwrites its map while the user is editing locally, applying the batch of edits will result in corrupted/misplaced geometry.
66+
* **Mitigation**: Implement a "Version Check" loop. Use map hashes/timestamps (if available in the status) to verify the robot's current geometry matches the one used to build the local Virtual State before executing the sync.
67+
68+
### 6. Multi-Floor Context Binding
69+
* **Risk**: Commands sent without explicit map flagging may apply to the currently active floor rather than the floor the user is editing.
70+
* **Mitigation**: Every command in the translation layer must be explicitly bound to the target `map_flag` ID.
71+
72+
---
73+
74+
## Technical Implementation Details
75+
76+
### The Coordinate Matrix (Algebraic Reversal)
77+
To ensure edits are placed correctly, the system must map between Image Space (PNG pixels) and Robot Space (mm). The parser `vacuum-map-parser-roborock` exposes an `ImageDimensions` object (`top`, `left`, `scale`) that defines the crop and zoom applied during rendering. It also divides the raw Robot Space by a factor of 50 to create its internal Grid Space.
78+
79+
**The Formula to translate a click on the PNG back to a Robot API command:**
80+
* `Robot_X = ((Pixel_X / dimensions.scale) + dimensions.left) * 50`
81+
* `Robot_Y = ((Pixel_Y / dimensions.scale) + dimensions.top) * 50`
82+
83+
The `Geometric Engine` must use these exact algebraic reversals to build the Translation Layer payloads. Failure to use the parser's dynamic `left`/`top` offsets will result in walls drifting outside of the rooms.
84+
85+
### Required Dependencies
86+
* **Geometry Math**: Evaluate `shapely` (heavy, robust) vs. a custom pure-python implementation for basic line-segment and bounding-box math.
87+
* **Rendering**: Enhancements to the existing `MapParser` to draw "pending" edits (e.g., dotted lines for proposed virtual walls).
88+
89+
### API Command Mapping Target
90+
The translation layer must reliably implement the following commands from `roborock/roborock_typing.py`:
91+
* `SPLIT_ROOM` / `MERGE_SEGMENT` / `RENAME_ROOM` / `SET_ROOM_ORDER`
92+
* `SET_VIRTUAL_WALL` / `SET_NO_GO_ZONES` / `SET_MOP_FORBIDDEN_ZONE`
93+
* `SET_CARPET_AREA` / `SET_SEGMENT_GROUND_MATERIAL` / `SAVE_FURNITURES`
94+
* `SAVE_MAP` (to commit the changes permanently)
95+
96+
### Geometric Translation Challenges
97+
The primary engineering hurdle is translating natural language ("the kitchen") into exact coordinates.
98+
1. The user identifies the "Kitchen" (or we list it via `RoomsTrait`).
99+
2. The Geometric Engine calculates the `[min_x, max_x, min_y, max_y]` of the Kitchen's pixels in the map array.
100+
3. We determine the user's intent (e.g., "split in half vertically").
101+
4. The Engine calculates the exact `x1, y1, x2, y2` coordinates that form a line cleanly intersecting the kitchen's walls without leaving the segment.
102+
103+
---
104+
105+
## Next Steps for Implementation
106+
107+
1. **[PRIORITY] Parser Exploration Script:** Write a targeted script (`roborock/map/explore_parser.py`) to pull a live map and inspect the `MapData` object for raw mathematical coordinates. This validates the feasibility of Phase 1.
108+
2. **Scaffold the Local State Machine:** Create `roborock/map/editor.py` with the `VirtualState` and `EditObject` base classes.
109+
3. **Polymorphic Translation Design:** Define the internal schema for V1 vs. A01/B01 map editing payloads.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# Qrevo Curv 2 Flow Control & Feedback Improvement Plan
2+
3+
Date: 2026-04-10
4+
Device: Qrevo Curv 2 Flow (`roborock.vacuum.a245`)
5+
Device ID: `7FjpPFODrdwoiHnJvL6eW2`
6+
7+
## Goal
8+
9+
Explain why robot control feels thin, document the commands that work, and define a better feedback plan for future robot operations.
10+
11+
## Executive summary
12+
13+
This repo exposes a thin pass-through API with a few helper commands. It is useful for sending raw device commands, but it does not provide much opinionated feedback, validation, or state-aware guidance.
14+
15+
The practical consequence is:
16+
- commands often return only `ok`,
17+
- command success does not mean the expected state change happened,
18+
- room-targeted cleaning requires trial-and-error on payload shape,
19+
- long-running jobs need manual status checks before/after execution.
20+
21+
## Why control feels weak
22+
23+
1. Commands are mostly raw RPC passthroughs.
24+
2. The CLI does not enforce state-aware workflows.
25+
3. Success responses are not rich enough to confirm intent.
26+
4. Job execution and cleaning state are not surfaced as first-class feedback.
27+
5. Some useful command schemas had to be discovered live.
28+
29+
## Commands already verified and what they showed
30+
31+
### Inspection / discovery
32+
33+
| Command | Outcome |
34+
| --- | --- |
35+
| `rooms` | Worked; returned the current room map |
36+
| `home` | Worked; returned cached home/layout data |
37+
| `maps` | Worked; returned map metadata |
38+
| `map-data --include_path` | Worked; returned JSON geometry/path data |
39+
| `map-image` | Worked; wrote an image file |
40+
| `status` | Worked; returned live device state |
41+
| `features` | Worked; returned capability flags |
42+
| `get_room_mapping` | Worked; returned room/segment mappings |
43+
| `get_customize_clean_mode` | Worked; returned per-segment clean settings |
44+
45+
### Job/control commands
46+
47+
| Command | Outcome |
48+
| --- | --- |
49+
| `app_start` | Worked |
50+
| `app_pause` | Worked |
51+
| `app_stop` | Worked |
52+
| `app_charge` | Worked |
53+
| `set_mop_mode [300]` | Worked |
54+
| `set_clean_motor_mode [{"fan_power":102}]` | Worked |
55+
| `set_customize_clean_mode [...]` | Worked |
56+
| `app_segment_clean [1]` | Worked; started a live cleaning run |
57+
58+
### Unsupported / broken
59+
60+
| Command | Outcome |
61+
| --- | --- |
62+
| `flow-led-status` | Unsupported |
63+
| `q10-*` session commands | Unsupported for this model |
64+
| `clean-record` | Broken CLI/API binding |
65+
66+
## Current live behavior observed
67+
68+
Most recent clean run showed:
69+
- `state: 23`
70+
- `inCleaning: 3`
71+
- `fanPower: 102`
72+
- `waterBoxStatus: 1`
73+
- `waterBoxMode: 235`
74+
- `mopMode: 300`
75+
- `washPhase: 13`
76+
77+
This suggests a mop-capable cleaning cycle, not a strongly opinionated vacuum-only workflow.
78+
79+
## Proposed structure for better feedback
80+
81+
### 1. Command outcome matrix
82+
83+
Add a durable table for:
84+
- verified working
85+
- unsupported
86+
- broken
87+
- untested
88+
89+
### 2. Pre/post status capture
90+
91+
For every actuation command, document:
92+
- pre-status
93+
- command payload
94+
- immediate response
95+
- post-status
96+
- visible physical outcome when relevant
97+
98+
### 3. Command recipe docs
99+
100+
Add copy-paste recipes for:
101+
- clean all rooms
102+
- clean all rooms except office
103+
- vacuum-only room run
104+
- return to dock safely
105+
106+
### 4. Feedback gaps
107+
108+
Track where the API is thin:
109+
- no job ID
110+
- no progress events
111+
- minimal error detail
112+
- limited schema discovery
113+
114+
## Follow-up docs to update
115+
116+
- `docs/qrevo-curv-2-flow-command-check.md`
117+
- `docs/plans/qrevo-curv-2-flow-remaining-live-tests.md`
118+
- `docs/V1_API_COMMANDS.md`
119+
120+
## Open questions
121+
122+
1. Which room is the office?
123+
2. Can the device expose richer command responses than `ok`?
124+
3. Is there a stable payload schema for `app_segment_clean` across all runs?
125+
126+
## Success criteria
127+
128+
- [ ] Command outcomes are documented clearly
129+
- [ ] Thin-API limitations are explained plainly
130+
- [ ] Feedback gaps are listed with concrete next steps
131+
- [ ] The remaining live tests can be traced back to a single plan

0 commit comments

Comments
 (0)