Initial room configuration support for Q10 devices#837
Conversation
… helpers Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for reading and refreshing room configuration for Q10 (B01) devices, including parsing the CUSTOMER_CLEAN payload and exposing room helpers on the properties API.
Changes:
- Introduces
RoomsTraitand wires it intoQ10PropertiesApisubscribe loop and helper methods. - Adds room-config parsing/container models for the
CUSTOMER_CLEANbase64 payload. - Adds tests covering streaming updates, listener notifications, and refresh requests for room config.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| tests/devices/traits/b01/q10/test_rooms.py | Adds integration-style tests for rooms streaming, listener behavior, and refresh request payload. |
| roborock/devices/traits/b01/q10/rooms.py | Implements RoomsTrait to refresh and parse room configuration updates and notify listeners. |
| roborock/devices/traits/b01/q10/init.py | Wires RoomsTrait into Q10PropertiesApi and adds convenience room helper APIs. |
| roborock/data/b01_q10/b01_q10_containers.py | Adds dataclasses and parsing/normalization for the room configuration payload. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| assert parsed.declared_count == 1 | ||
| assert parsed.parsed_count == 1 | ||
| room = parsed.rooms[0] |
There was a problem hiding this comment.
Can you add a test for an instance with multiple rooms?
|
|
||
| for index in range(count): | ||
| if offset + _Q10_CUSTOMER_CLEAN_ROOM_BLOCK_SIZE > len(raw): | ||
| break |
There was a problem hiding this comment.
So I'm not sure about all of theses breaks. if one step fails, we are returning partial data, which I think can be problematic.
There was a problem hiding this comment.
Also we could just check them all at once. Seems like the total length be the count * (26+20+1] then read vertex count then check its vertex_count * _Q10_CUSTOMER_CLEAN_VERTEX_SIZE, then we don't ned to check again.
There was a problem hiding this comment.
Wonder if using construct would be helpful like how some of the other packages are parsed in protocol.py...
| rooms: RoomsTrait | ||
| """Trait for reading room configuration from Q10 devices.""" | ||
|
|
||
| @property |
There was a problem hiding this comment.
Seems like these should just be on RoomsTrait and not here
| # to the device. Updates will be received by the subscribe loop below. | ||
| await self.command.send(B01_Q10_DP.REQUEST_DPS, params={}) | ||
|
|
||
| async def refresh_room_config(self) -> None: |
There was a problem hiding this comment.
Similar here, these should all be on the rooms trait
| return {room.room_id: room for room in self.rooms} | ||
|
|
||
|
|
||
| def parse_customer_clean_payload(payload_b64: str) -> Q10RoomsConfig: |
There was a problem hiding this comment.
This seems to me like it fits better next to RoomsTrait.
|
|
||
|
|
||
| def normalize_q10_room_name(room_name: str) -> str: | ||
| """Normalize room names reported by the Q10 firmware.""" |
There was a problem hiding this comment.
Maybe would be good to give some examples in the pydoc of what this is doing.
| return Q10RoomsConfig(raw_length=len(raw), declared_count=count, rooms=rooms) | ||
|
|
||
|
|
||
| def normalize_q10_room_name(room_name: str) -> str: |
There was a problem hiding this comment.
Could also consider making this a property on Q10RoomConfig which exposes the name (e.g.raw_name and name as a property.)
|
|
||
| for index in range(count): | ||
| if offset + _Q10_CUSTOMER_CLEAN_ROOM_BLOCK_SIZE > len(raw): | ||
| break |
There was a problem hiding this comment.
Also we could just check them all at once. Seems like the total length be the count * (26+20+1] then read vertex count then check its vertex_count * _Q10_CUSTOMER_CLEAN_VERTEX_SIZE, then we don't ned to check again.
|
|
||
| for index in range(count): | ||
| if offset + _Q10_CUSTOMER_CLEAN_ROOM_BLOCK_SIZE > len(raw): | ||
| break |
There was a problem hiding this comment.
Wonder if using construct would be helpful like how some of the other packages are parsed in protocol.py...
| class Q10RoomConfig(RoborockBase): | ||
| index: int | ||
| room_id: int | ||
| room_type: int |
There was a problem hiding this comment.
Are these all some kind of enum value? trying to reason about what "-1" will mean here and wondering if int | None would be better or RoomTypeEnum | None if we can figure those out.
Summary
This PR adds initial room configuration support for Q10 devices using the B01 protocol.
It introduces parsing for the
CUSTOMER_CLEANpayload, exposes a dedicated Q10 rooms trait, and wires that trait intoQ10PropertiesApiso room configuration can be refreshed and accessed through high-level helpers. It also adds focused test coverage for room parsing, streaming updates, and the new API helpers.What Changed
refresh_room_config()room_maproom_namesget_room()get_room_name()Behavior
rr_*firmware room names are normalized into user-friendly labels such asLiving RoomValidation
4 passed