-
Notifications
You must be signed in to change notification settings - Fork 62
feat(q10): Add Q10 vacuum CLI commands and Status API #759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds CLI commands to control Roborock Q10 vacuum operations (start/pause/resume/stop/dock) from the roborock command line tool.
Changes:
- Added five new Click commands for Q10 vacuum control.
- Wired the new commands into the root CLI command registry.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async def refresh(self) -> dict[B01_Q10_DP, Any]: | ||
| """Refresh status values from the device.""" | ||
| decoded = await send_decoded_command( | ||
| self._channel, | ||
| command=B01_Q10_DP.REQUETDPS, | ||
| params={}, | ||
| expected_dps={B01_Q10_DP.STATUS, B01_Q10_DP.BATTERY}, | ||
| ) | ||
| self._data = decoded | ||
| return decoded |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StatusTrait is new and introduces device-state parsing/refresh behavior, but there are no unit tests covering refresh() and the typed property accessors (state/battery/fan_level/etc.). The repo already has Q10 trait tests (e.g., tests/devices/traits/b01/q10/test_vacuum.py); please add similar tests for status responses (including enum decoding) to prevent regressions.
| async def send_decoded_command( | ||
| mqtt_channel: MqttChannel, | ||
| command: B01_Q10_DP, | ||
| params: ParamsType, | ||
| expected_dps: Iterable[B01_Q10_DP] | None = None, | ||
| ) -> dict[B01_Q10_DP, Any]: | ||
| """Send a command and await the first decoded response. | ||
|
|
||
| Q10 responses are not correlated with a message id, so we filter on | ||
| expected datapoints when provided. | ||
| """ |
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send_decoded_command() adds new request/response correlation logic (expected datapoint filtering + timeout), but there are no unit tests validating: (1) it returns the decoded dps for a matching response, (2) it ignores non-matching responses when expected_dps is set, and (3) it raises on timeout. There are existing channel tests (e.g., tests/devices/rpc/test_a01_channel.py); please add analogous tests for the B01 Q10 channel to lock in behavior.
| raise | ||
|
|
||
|
|
||
| async def send_decoded_command( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work? My impression is that q10 doesn't support this kind of mapping between request and response. We have a different approach in
#709
| @property | ||
| def state(self) -> YXDeviceState | None: | ||
| code = self.state_code | ||
| return cast(YXDeviceState | None, YXDeviceState.from_code_optional(code)) if code is not None else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cast should not be needed after this change #761
- Add type annotations to Q10 command parameters (ctx: click.Context, device_id: str) - Add return type annotations (-> None, -> VacuumTrait) for all Q10 commands - Change Q10 commands from @click.command() to @session.command() for session mode availability - Add VacuumTrait import for proper type hints - Create comprehensive StatusTrait tests (5 tests covering refresh, properties, enums) - Create comprehensive send_decoded_command tests (6 tests covering success, filtering, timeout, errors) Resolves all 6 unresolved comments from code review
- Add type annotations to Q10 command parameters (ctx: click.Context, device_id: str) - Add return type annotations (-> None, -> VacuumTrait) for all Q10 commands - Change Q10 commands from @click.command() to @session.command() for session mode availability - Add VacuumTrait import for proper type hints - Create comprehensive StatusTrait tests (5 tests covering refresh, properties, enums) - Create comprehensive send_decoded_command tests (6 tests covering success, filtering, timeout, errors) Resolves all 6 unresolved comments from code review
829d338 to
ff4d425
Compare
- Add q10-vacuum-start command to start vacuum cleaning - Add q10-vacuum-pause command to pause vacuum cleaning - Add q10-vacuum-resume command to resume vacuum cleaning - Add q10-vacuum-stop command to stop vacuum cleaning - Add q10-vacuum-dock command to return vacuum to dock - All commands require device_id parameter - Commands validate that device has VacuumTrait (Q10 only)
- Add _q10_vacuum_trait() helper function similar to _v1_trait() - Refactor all 5 Q10 commands to use the helper - Centralizes device lookup and trait validation logic - Addresses code review feedback from copilot-pull-request-reviewer
- Add try/except blocks to catch RoborockUnsupportedFeature - Display user-friendly error messages instead of stack traces - Follow same pattern as _display_v1_trait for expected errors - Addresses code review feedback from copilot-pull-request-reviewer
- Change from device.traits.get('VacuumTrait') to device.b01_q10_properties.vacuum
- Fix AttributeError that would occur at runtime
- Update error messages to reflect B01 Q10 protocol requirement
- Addresses code review feedback from copilot-pull-request-reviewer
- Add type annotations to Q10 command parameters (ctx: click.Context, device_id: str) - Add return type annotations (-> None, -> VacuumTrait) for all Q10 commands - Change Q10 commands from @click.command() to @session.command() for session mode availability - Add VacuumTrait import for proper type hints - Create comprehensive StatusTrait tests (5 tests covering refresh, properties, enums) - Create comprehensive send_decoded_command tests (6 tests covering success, filtering, timeout, errors) Resolves all 6 unresolved comments from code review
These files are auto-generated/updated on merge to main, per project guidelines. Reverting to their state before this PR.
ff4d425 to
28716c9
Compare
feat(q10): Add Q10 vacuum CLI commands and Status API
Overview
This PR adds complete CLI command support for Roborock Q10 vacuum devices including five vacuum control commands and a new Status API trait for reading device state. All code review feedback has been addressed, and the implementation is tested on a real Q10 S5+ device.
What's Changed
1. CLI Commands for Q10 Vacuum Control
Added 5 new Click commands to control Q10 devices from the CLI:
All commands:
--device_idparameter_q10_vacuum_trait()helper to reduce code duplication2. Status API for Q10 Devices
Implemented a new
StatusTraitto read Q10 device state with properties for:state- Device state (sleepstate, charging, cleaning, etc.)battery- Battery percentage (0-100)fan_level- Fan level (quiet, standard, strong, max, super, custom)water_level- Water level for mopping (low, medium, high)clean_mode- Cleaning mode (quiet, balanced, standard, high)clean_task- Task type (unknown, room, goroom, zone, area, spot)cleaning_progress- Progress percentage3. Decoded MQTT Response Handler
Added
send_decoded_command()for handling Q10's MQTT request/response pattern:4. Fan Level Mapping Enhancement
Added missing
YXFanLevel.CUSTOMmapping for code 8 to handle all Q10 fan modes.Code Quality & Testing
✅ All 7 code review comments addressed and resolved
✅ All 5 CLI commands tested on real Q10 S5+ device
✅ Status API tested and working (battery=91%, state=sleepstate, fan_level=custom)
✅ All pre-commit checks passing (ruff format, mypy types, debug checks)
✅ All CI tests passing (lint, test 3.11, test 3.14, build, semantic release)
✅ Code duplication eliminated with reusable helpers
✅ Proper error handling with user-friendly messages
Files Changed
roborock/cli.py- Added 5 Q10 commands +_q10_vacuum_trait()helperroborock/devices/traits/b01/q10/status.py- NEW StatusTrait class (79 lines)roborock/devices/rpc/b01_q10_channel.py- Addedsend_decoded_command()(61 lines)roborock/devices/traits/b01/q10/__init__.py- Exposed StatusTrait in APIroborock/data/b01_q10/b01_q10_code_mappings.py- Added YXFanLevel.CUSTOMUsage Examples
CLI Commands
Status API
Interactive Session
Related