Skip to content

q7: add map_content trait for B01 current map#4

Draft
arduano wants to merge 37 commits intomainfrom
leo/q7-map-content-followup
Draft

q7: add map_content trait for B01 current map#4
arduano wants to merge 37 commits intomainfrom
leo/q7-map-content-followup

Conversation

@arduano
Copy link
Owner

@arduano arduano commented Mar 15, 2026

Summary

Combine the old split-2/split-3 follow-up into one reviewable fork PR that adds a Q7/B01 map_content trait on top of the already-merged raw map transport work.

This PR:

  • adds a Q7 map_content trait with the same general refresh/cached-fields pattern used by v1
  • wires B01/Q7 trait creation through device_manager with access to device/product metadata needed for map decoding
  • adds a minimal internal B01 map parser that:
    • decodes the raw MAP_RESPONSE payload
    • parses the inflated SCMap payload
    • renders a PNG image
    • exposes room metadata through MapData.rooms
  • adds parser + trait tests and fixture data

Design intent

The goal here is to keep the new work aligned with the existing codebase shape rather than inventing a separate parser/integration framework:

  • transport stays in the existing Q7 map trait
  • parsed map access lives in a dedicated map_content trait
  • refresh() performs I/O and populates cached fields
  • parsing internals stay private/minimal

Validation

  • ruff check on touched files
  • pytest -q → 395 passed
  • live read-only sanity check on Leo's real Q7 account/device:
    • device: Roborock Q7T+ (roborock.vacuum.sc05)
    • map fetch/decode/render succeeded
    • rendered PNG bytes: 17457
    • raw payload bytes: 35584
    • rooms discovered: 10..19 / room1..room10

Notes

This is fork-only for review first.

@arduano
Copy link
Owner Author

arduano commented Mar 15, 2026

A note on the SCMap decode approach, since I explicitly looked at whether this should be modeled as "real protobuf" instead of the current tiny wire parser.

Short version:

  • the outer Q7 MAP_RESPONSE payload is not protobuf; it is the observed custom transport shape we validated live:
    • base64 ASCII
    • AES-ECB with the derived map key
    • PKCS7 padding
    • ASCII hex
    • zlib
  • the inner inflated SCMap blob does appear to use protobuf wire encoding

I did consider switching the inner decode to generated protobuf classes, but I decided against that for this PR.

Why:

  1. We do not have an upstream .proto schema for SCMap.
  2. Introducing generated protobuf code here would mean first reverse-engineering and committing a guessed schema.
  3. That would imply more confidence in the undocumented format than we actually have.
  4. For the small field set this PR currently needs (map dimensions, occupancy raster, room ids/names), a tiny schema-free wire parser is actually the smaller and more honest implementation.

So the current choice is intentional:

  • keep runtime parsing narrow and explicit
  • only parse the specific fields we need
  • avoid adding reverse-engineered schema baggage to the library

To make future debugging less painful, I added a small dev-only helper script at tests/map/debug_b01_scmap.py.
It is not imported by runtime code; it just gives us a convenient place to inspect new payload samples / field layouts when we need to learn more about SCMap without expanding the runtime abstraction prematurely.

If maintainers would prefer a different home for that helper, or would rather keep even that out of the repo, I’m happy to trim it further.

Openclaw:

  • verified on a live Q7T+ MAP_RESPONSE sample before keeping the narrowed decode path
  • kept the parser schema-free on purpose to avoid overcommitting to a reverse-engineered protobuf model

@arduano arduano force-pushed the leo/q7-map-content-followup branch 2 times, most recently from a81db61 to 67fc042 Compare March 15, 2026 02:15
@arduano arduano force-pushed the leo/q7-map-content-followup branch from 67fc042 to 487778a Compare March 15, 2026 02:18
@arduano arduano force-pushed the leo/q7-map-content-followup branch 4 times, most recently from 8ef5515 to 2c839e0 Compare March 15, 2026 06:35
@arduano arduano force-pushed the leo/q7-map-content-followup branch from 2c839e0 to 2d73fd1 Compare March 15, 2026 06:39
@arduano arduano force-pushed the leo/q7-map-content-followup branch from 6b3ec67 to bedf379 Compare March 16, 2026 05:21
arduano and others added 15 commits March 16, 2026 16:26
…erge (Python-roborock#783)

* feat: Simplify V1 trait handling

* Draft to set trait values

* refactor: remove trait update listeners and centralize data conversion into dedicated converter classes

* chore: Remove unused `typing.Self` import.

* refactor: Standardize trait data merging to `merge_trait_values` and remove direct `_parse_response` methods from traits.

* docs: clarify internal usage of V1TraitDataConverter and V1TraitMixin attributes.

* style: Remove trailing whitespace from a blank line in `common.py` docstring.

* refactor: Make V1TraitDataConverter an abstract base class, use a dedicated LedStatusConverter, and fix a typo in Rooms.

* chore: Remove duplicate V1TraitDataConverter
Automatically generated by python-semantic-release
* chore: Rename and reorder `YXFanLevel` enum members

* chore: Fix MAX_PLUS enum value

* docs: Add docstring and alias comments to the YXFanLevel enum.

* chore: fix lint.
Automatically generated by python-semantic-release
…borock#788)

* feat: Add `from_any_optional` method to `CodeMapping` for flexible enum resolution with corresponding tests.

* refactor: simplify B01_Q10 command parsing by removing a helper function and utilizing `from_any_optional`.

* chore: apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Luke Lashley <conway220@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Automatically generated by python-semantic-release
* feat: Q7 Get battery level

* feat: Q7 Get battery level

* Run lint
Automatically generated by python-semantic-release
arduano and others added 14 commits March 19, 2026 14:53
)

* chore: increase commit header maximum length to 200 in commitlint configuration.

* chore: Disable commitlint rules for header max length and header full stop.
…ng (Python-roborock#791)

* fix: add missing DPS fields to Q10Status and fix CLEAN_PROGRESS mapping

* fix: correct comment for fan level in test_status_trait_refresh
Automatically generated by python-semantic-release
Automatically generated by python-semantic-release
…instead of legacy `*state` strings (Python-roborock#793)

* fix(q10): normalize status names to canonical values

* fix(q10): normalize YXDeviceState status names to canonical values

* fix(q10): refactor test for canonical status names using a dictionary

* fix(q10): add tests for Q10 status values and code mappings
Automatically generated by python-semantic-release
…ency with V1 state values (Python-roborock#795)

The motivation is to not have entirely different state values for the same concept to make it easier to write systems that can handle each device.
…thon-roborock#794)

* refactor(q10): use readable YXCleanType values with legacy aliases

* fix(cli): make clean mode option case insensitive

* refactor(YXCleanType): move legacy values to a separate dictionary and update from_value method

* test(YXCleanType): add tests for legacy clean type string aliases

* refactor(YXCleanType): simplify clean type definitions and remove legacy alias support

* test(YXCleanType): add tests for readable public values and compatibility with aliases

* test(YXCleanType): add compatibility test for readable values in from_value method

* test(q10): update clean type code mapping tests

Co-authored-by: Allen Porter <allen.porter@gmail.com>

* fix(q10): restrict clean mode cli choices

* refactor(YXCleanType): remove legacy test for readable public values

* test(YXCleanType): update test for readable values to use canonical names

* test(YXCleanType): simplify test for readable values in from_value method

---------

Co-authored-by: Allen Porter <allen.porter@gmail.com>
Automatically generated by python-semantic-release
…hon-roborock#798)

* chore: Migrate to `typing.Self` and remove `__future__` annotations.

* chore: Use `typing.Self` for class-referencing type hints and dynamic instantiation

* chore: Update roborock/roborock_message.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@arduano arduano force-pushed the leo/q7-map-content-followup branch from 2dfd139 to 5d19def Compare March 23, 2026 03:00
@arduano arduano force-pushed the leo/q7-map-content-followup branch from 5d19def to 99a63e6 Compare March 23, 2026 03:02
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.

4 participants