diff --git a/roborock/cli.py b/roborock/cli.py index c7366275..9def26af 100644 --- a/roborock/cli.py +++ b/roborock/cli.py @@ -1482,6 +1482,39 @@ async def q10_vacuum_spot(ctx: click.Context, device_id: str) -> None: click.echo(f"Error: {e}") +@session.command() +@click.option("--device_id", required=True, help="Device ID") +@click.option( + "--segments", + required=True, + help="Comma-separated room/segment ids to clean (see the `rooms` command), e.g. 9,2", +) +@click.pass_context +@async_command +async def q10_clean_segments(ctx: click.Context, device_id: str, segments: str) -> None: + """Start a room / segment clean on a Q10 device. + + Room ids come from the `rooms` command (the device's map rooms). + """ + context: RoborockContext = ctx.obj + try: + segment_ids = [int(s) for s in segments.split(",") if s.strip()] + except ValueError: + click.echo("--segments must be comma-separated integers, e.g. 9,2") + return + if not segment_ids: + click.echo("No segment ids provided") + return + try: + trait = await _q10_vacuum_trait(context, device_id) + await trait.clean_segments(segment_ids) + click.echo(f"Starting room clean of segments {segment_ids}...") + except RoborockUnsupportedFeature: + click.echo("Device does not support B01 Q10 protocol. Is it a Q10?") + except RoborockException as e: + click.echo(f"Error: {e}") + + async def _q10_set(ctx: click.Context, device_id: str, apply: Callable[[Any], Any], message: str) -> None: """Run a Q10 settings write and report the result.""" context: RoborockContext = ctx.obj diff --git a/roborock/devices/traits/b01/q10/vacuum.py b/roborock/devices/traits/b01/q10/vacuum.py index fbe447de..2747e024 100644 --- a/roborock/devices/traits/b01/q10/vacuum.py +++ b/roborock/devices/traits/b01/q10/vacuum.py @@ -3,6 +3,7 @@ from roborock.data.b01_q10.b01_q10_code_mappings import ( B01_Q10_DP, YXCleanType, + YXDeviceCleanTask, YXFanLevel, ) @@ -23,17 +24,38 @@ def __init__(self, command: CommandTrait) -> None: async def start_clean(self) -> None: """Start a whole-home clean. - The ``dpStartClean`` (201) command takes a bare integer task code: - ``1`` = whole-home, ``2`` = segment/room, ``3`` = zone, ``4`` = build map, - ``5`` = spot. Whole-home and spot take no extra parameters; segment and - zone need a room/zone selection whose payload shape is not yet known, so - only whole-home (here) and spot (:meth:`spot_clean`) are exposed. + The ``dpStartClean`` (201) command selects a task by code: ``1`` = + whole-home, ``2`` = segment/room (see :meth:`clean_segments`), ``3`` = + zone, ``4`` = build map, ``5`` = spot. Whole-home and spot accept the + bare integer code; segment cleaning needs a room selection (an object + payload) instead. Verified live against ss07 hardware: ``{"dps": {"201": 1}}`` starts a whole-home clean (clean_task_type -> 1). """ await self._command.send(command=B01_Q10_DP.START_CLEAN, params=1) + async def clean_segments(self, segment_ids: list[int]) -> None: + """Start a room / segment clean for the given segment (room) ids. + + The ids are the same room ids the device reports on its map (see the Q10 + ``MapContentTrait`` -- ``map.rooms``, each with an ``id``). + + Unlike whole-home and spot, ``dpStartClean`` (201) carries the room + selection as an object: ``{"cmd": , "clean_paramters": [, ...]}``, + where ``cmd`` is the segment-clean task code. + + Verified live against ss07 hardware: sending + ``{"dps": {"201": {"cmd": 2, "clean_paramters": [9]}}}`` starts cleaning + room 9 (clean_task_type -> 2 / electoral). + """ + await self._command.send( + command=B01_Q10_DP.START_CLEAN, + # "clean_paramters" intentionally mirrors the device's misspelling of + # "parameters" -- the firmware only accepts that exact key. + params={"cmd": YXDeviceCleanTask.ELECTORAL.code, "clean_paramters": segment_ids}, + ) + async def spot_clean(self) -> None: """Start a spot / part clean around the robot's current position. diff --git a/tests/devices/traits/b01/q10/test_vacuum.py b/tests/devices/traits/b01/q10/test_vacuum.py index e736d28c..17f6b3de 100644 --- a/tests/devices/traits/b01/q10/test_vacuum.py +++ b/tests/devices/traits/b01/q10/test_vacuum.py @@ -30,6 +30,8 @@ def vacuumm_fixture(q10_api: Q10PropertiesApi) -> VacuumTrait: [ # Payloads verified live against ss07 hardware. (lambda x: x.start_clean(), {"201": 1}), + (lambda x: x.clean_segments([9]), {"201": {"cmd": 2, "clean_paramters": [9]}}), + (lambda x: x.clean_segments([1, 2]), {"201": {"cmd": 2, "clean_paramters": [1, 2]}}), (lambda x: x.spot_clean(), {"201": 5}), (lambda x: x.pause_clean(), {"204": 0}), (lambda x: x.resume_clean(), {"205": 0}),