Skip to content

feat: expose Q10 map archives - #937

Open
hCoureau wants to merge 17 commits into
Python-roborock:mainfrom
hCoureau:feat/q10-map-archives
Open

hCoureau wants to merge 17 commits into
Python-roborock:mainfrom
hCoureau:feat/q10-map-archives

Conversation

@hCoureau

@hCoureau hCoureau commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Q10 archives arrive on independent push streams. This adds read-only saved-map previews and clean-history map/path details while keeping archive data separate from the live map.

Public API and data flow

  • maps owns saved-map metadata and the latest received preview. detail_map_id comes from the pushed packet itself.
  • clean_history inherits its public data and derived accessors from the CleanHistory dataclass, a RoborockBase model. It exposes the latest received archive map, historical path and rendered bytes.
  • Both refresh_detail() methods return after publishing. Device pushes populate the read models and notify add_update_listener() subscribers. An asyncio.Lock serializes publication only; missing responses do not block later requests.
  • Clean-history selections use the complete raw firmware record identifier. Saved-map selections validate the ID against the received map list.
  • Q10PropertiesApi routes typed Q10CleanRecordDetail and saved-map packets to their archive owners. Archive updates cannot replace live-map state.
  • The factory supplies shared rendering configuration to all three map views. Archive constructors require concrete configuration; default configuration stays at the factory boundary.
  • Diagnostic serialization includes history records/path data and saved-map metadata while excluding binary map grids and PNG bytes. Images remain available through detail_image_content.

Consumer expectations and protocol limits

Subscribe before requesting an archive, then read the trait when its listener fires. roborock/devices/README.md documents persistent-view usage and a one-shot listener/event example with a consumer-owned deadline. Trait methods do not create response-waiting tasks or futures.

Clean-record detail packets carry no record identifier. Delayed pushes and selections from another client cannot be attributed reliably to a local selection, so this API exposes latest received detail without a detail_record association. Saved-map packets identify the map, but contain no request ID; consumers should inspect detail_map_id before displaying a preview. A later received preview replaces the previous preview regardless of arrival order.

Live-map updates use the normal trait listener API without revision counters. The CLI subscribes before requesting a push, waits for an update satisfying its state predicate, and unsubscribes on success, timeout or cancellation. Its existing 30-second deadline and optional cached-map fallback remain at the CLI boundary.

Dependencies

Archive parser support from #936 is merged, alongside #933, #908 and #965. This branch includes upstream main through b3a98b9 (7.12.0).

Validation

Validation of f1f5e24:

  • Full suite on Python 3.11 and 3.14: 1,440 passed, 53 xfailed, 92 snapshots passed on each version.
  • All pre-commit hooks passed, including Ruff, mypy, codespell and structured-file checks.
  • Wheel and source distribution builds passed.
  • Fake-channel tests cover selection commands, publication serialization, cancellation/send-failure recovery, missing responses, unsolicited/repeated pushes, listener notifications, live-map isolation and diagnostic serialization.
  • Render configuration tests check actual PNG dimensions at scales 1, 2 and 4.

No physical-device run was performed for this revision. No private map captures or account data are included. Related: #767.

Comment thread roborock/devices/traits/b01/q10/clean_history.py Outdated
Comment thread roborock/devices/traits/b01/q10/clean_history.py Outdated
Comment thread roborock/devices/traits/b01/q10/map.py Outdated
Comment thread tests/devices/traits/b01/q10/test_map.py Outdated
Comment thread tests/devices/traits/b01/q10/test_map.py Outdated
@hCoureau

hCoureau commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all five review comments in f1f5e24 and pushed the changes to this branch.

The archive traits now follow the existing push/listener contract: selections return after publication, asyncio.Lock serializes sends, and received archives update the read models and notify subscribers. Clean-history data is in a RoborockBase dataclass and included in diagnostic serialization. Removed the response-waiting machinery, revision counters and unsupported clean-record request attribution. Added consumer documentation, fake-channel command/lifecycle tests and PNG-dimension tests for shared render configuration.

Validation: 1,440 passed, 53 expected failures and 92 snapshots passed on both Python 3.11 and 3.14; all pre-commit hooks and both package builds passed. The PR description now reflects the final API and protocol limitations.

@hCoureau
hCoureau requested a review from allenporter October 1, 2026 21:10
## Q10 archive updates

Q10 traits are push driven. `refresh_detail()` publishes a selection and returns
when the command has been sent. It does not wait for the map response. Register

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this the same as all other traits for q10 right?

I don't think we should encourage people to build blocking updates on this. Instead they can just update their object state when we invoke their callback...

) -> None:
record = CleanRecordConverter.parse_record(RECORD_A)
assert record is not None
if failure == "cancel":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets not have a conditional inside the test. just make a separate test instead.

FWIW i'm not sure the concurrency is worth testing.



async def test_refresh_detail_sends_full_raw_record(
async def test_refresh_detail_publishes_without_waiting_for_push(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"test_refresh_detail_publishes_without_waiting_for_push" is not a useful concept for q10. No rpc or refresh commands wait for a push. This is just recording the history of your conversation in a test, not adding direct value.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants