From faf6226ffca7122957107384e5d7fb94a2f6e33a Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 20:37:53 -0800 Subject: [PATCH 1/8] Add VacuumTrait to q10 devices --- roborock/devices/traits/b01/q10/__init__.py | 14 ++++- roborock/devices/traits/b01/q10/vacuum.py | 60 +++++++++++++++++++++ tests/devices/traits/b01/q10/test_vacuum.py | 50 +++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 roborock/devices/traits/b01/q10/vacuum.py create mode 100644 tests/devices/traits/b01/q10/test_vacuum.py diff --git a/roborock/devices/traits/b01/q10/__init__.py b/roborock/devices/traits/b01/q10/__init__.py index 9073bf60..5972c21e 100644 --- a/roborock/devices/traits/b01/q10/__init__.py +++ b/roborock/devices/traits/b01/q10/__init__.py @@ -1,12 +1,18 @@ """Traits for Q10 B01 devices.""" +import asyncio +import logging from typing import Any -from roborock.devices.rpc.b01_q7_channel import send_decoded_command +from roborock import B01Props +from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP from roborock.devices.traits import Trait from roborock.devices.transport.mqtt_channel import MqttChannel from .command import CommandTrait +from .vacuum import VacuumTrait + +_LOGGER = logging.getLogger(__name__) __all__ = [ "Q10PropertiesApi", @@ -19,9 +25,15 @@ class Q10PropertiesApi(Trait): command: CommandTrait """Trait for sending commands to Q10 devices.""" + vacuum: VacuumTrait + """Trait for sending Vacuum related commands to Q10 devices""" + def __init__(self, channel: MqttChannel) -> None: """Initialize the B01Props API.""" self.command = CommandTrait(channel) + self.vacuum = VacuumTrait(self.command) + self._channel = channel + self._task: asyncio.Task | None = None def create(channel: MqttChannel) -> Q10PropertiesApi: diff --git a/roborock/devices/traits/b01/q10/vacuum.py b/roborock/devices/traits/b01/q10/vacuum.py new file mode 100644 index 00000000..71f41e11 --- /dev/null +++ b/roborock/devices/traits/b01/q10/vacuum.py @@ -0,0 +1,60 @@ +"""Traits for Q10 B01 devices.""" + +import logging + +from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP + +from .command import CommandTrait + +_LOGGER = logging.getLogger(__name__) + + +class VacuumTrait: + """Trait for sending vacuum commands. + + This is wrapper around the CommandTrait for sending vacuum related + commands to Q10 devices. + """ + + def __init__(self, command: CommandTrait) -> None: + """Initialize the CommandTrait.""" + self._command = command + + async def start_clean(self) -> None: + """Start cleaning.""" + await self._command.send( + command=B01_Q10_DP.START_CLEAN, + # TODO: figure out other commands + # 1 = start cleaning + # 2 = "electoral" clean, also has "clean_parameters" + # 4 = fast create map + params={"cmd": 1}, + ) + + async def pause_clean(self) -> None: + """Pause cleaning.""" + await self._command.send( + command=B01_Q10_DP.PAUSE, + params={}, + ) + + async def resume_clean(self) -> None: + """Resume cleaning.""" + await self._command.send( + command=B01_Q10_DP.RESUME, + params={}, + ) + + async def stop_clean(self) -> None: + """Stop cleaning.""" + await self._command.send( + command=B01_Q10_DP.STOP, + params={}, + ) + + async def return_to_dock(self) -> None: + """Return to dock.""" + await self._command.send( + command=B01_Q10_DP.START_DOCK_TASK, + params={}, + ) diff --git a/tests/devices/traits/b01/q10/test_vacuum.py b/tests/devices/traits/b01/q10/test_vacuum.py new file mode 100644 index 00000000..2bc6555f --- /dev/null +++ b/tests/devices/traits/b01/q10/test_vacuum.py @@ -0,0 +1,50 @@ +import json +from collections.abc import Awaitable, Callable +from typing import Any + +import pytest + +from roborock.devices.traits.b01.q10 import Q10PropertiesApi +from roborock.devices.traits.b01.q10.vacuum import VacuumTrait +from tests.fixtures.channel_fixtures import FakeChannel + + +@pytest.fixture(name="fake_channel") +def fake_channel_fixture() -> FakeChannel: + return FakeChannel() + + +@pytest.fixture(name="q10_api") +def q10_api_fixture(fake_channel: FakeChannel) -> Q10PropertiesApi: + return Q10PropertiesApi(fake_channel) # type: ignore[arg-type] + + +@pytest.fixture(name="vacuumm") +def vacuumm_fixture(q10_api: Q10PropertiesApi) -> VacuumTrait: + return q10_api.vacuum + + +@pytest.mark.parametrize( + ("command_fn", "expected_payload"), + [ + (lambda x: x.start_clean(), {"201": {"cmd": 1}}), + (lambda x: x.pause_clean(), {"204": {}}), + (lambda x: x.resume_clean(), {"205": {}}), + (lambda x: x.stop_clean(), {"206": {}}), + (lambda x: x.return_to_dock(), {"203": {}}), + ], +) +async def test_q7_api_set_fan_speed( + vacuumm: VacuumTrait, + fake_channel: FakeChannel, + command_fn: Callable[[VacuumTrait], Awaitable[None]], + expected_payload: dict[str, Any], +) -> None: + """Test sending a vacuum start command.""" + await command_fn(vacuumm) + + assert len(fake_channel.published_messages) == 1 + message = fake_channel.published_messages[0] + assert message.payload + payload_data = json.loads(message.payload.decode()) + assert payload_data == {"dps": expected_payload} From fb69579345aa3cfa7ed530600cc21a1c080302a8 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:12:09 -0800 Subject: [PATCH 2/8] Remove unused logger --- roborock/devices/traits/b01/q10/vacuum.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/roborock/devices/traits/b01/q10/vacuum.py b/roborock/devices/traits/b01/q10/vacuum.py index 71f41e11..8f6ecb23 100644 --- a/roborock/devices/traits/b01/q10/vacuum.py +++ b/roborock/devices/traits/b01/q10/vacuum.py @@ -1,13 +1,9 @@ """Traits for Q10 B01 devices.""" -import logging - from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP from .command import CommandTrait -_LOGGER = logging.getLogger(__name__) - class VacuumTrait: """Trait for sending vacuum commands. From 0fce06a50a3f2299210dfd48cd5ec7f496fa24b2 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:12:42 -0800 Subject: [PATCH 3/8] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- roborock/devices/traits/b01/q10/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/roborock/devices/traits/b01/q10/__init__.py b/roborock/devices/traits/b01/q10/__init__.py index 5972c21e..09b6c916 100644 --- a/roborock/devices/traits/b01/q10/__init__.py +++ b/roborock/devices/traits/b01/q10/__init__.py @@ -4,8 +4,6 @@ import logging from typing import Any -from roborock import B01Props -from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP from roborock.devices.traits import Trait from roborock.devices.transport.mqtt_channel import MqttChannel @@ -26,7 +24,7 @@ class Q10PropertiesApi(Trait): """Trait for sending commands to Q10 devices.""" vacuum: VacuumTrait - """Trait for sending Vacuum related commands to Q10 devices""" + """Trait for sending vacuum related commands to Q10 devices.""" def __init__(self, channel: MqttChannel) -> None: """Initialize the B01Props API.""" From 6e6df0b45f16df947205860545dfbe9a6a7dcefd Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:13:25 -0800 Subject: [PATCH 4/8] Fix lint errors --- roborock/devices/traits/b01/q10/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/roborock/devices/traits/b01/q10/__init__.py b/roborock/devices/traits/b01/q10/__init__.py index 5972c21e..6c67106b 100644 --- a/roborock/devices/traits/b01/q10/__init__.py +++ b/roborock/devices/traits/b01/q10/__init__.py @@ -1,11 +1,7 @@ """Traits for Q10 B01 devices.""" -import asyncio import logging -from typing import Any -from roborock import B01Props -from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP from roborock.devices.traits import Trait from roborock.devices.transport.mqtt_channel import MqttChannel @@ -32,8 +28,6 @@ def __init__(self, channel: MqttChannel) -> None: """Initialize the B01Props API.""" self.command = CommandTrait(channel) self.vacuum = VacuumTrait(self.command) - self._channel = channel - self._task: asyncio.Task | None = None def create(channel: MqttChannel) -> Q10PropertiesApi: From 7b4b966ec9e154c8037e09e16e9887f8040322b4 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:14:18 -0800 Subject: [PATCH 5/8] Remove unused logger --- roborock/devices/traits/b01/q10/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/roborock/devices/traits/b01/q10/__init__.py b/roborock/devices/traits/b01/q10/__init__.py index b77510ee..ac897259 100644 --- a/roborock/devices/traits/b01/q10/__init__.py +++ b/roborock/devices/traits/b01/q10/__init__.py @@ -1,15 +1,11 @@ """Traits for Q10 B01 devices.""" -import logging - from roborock.devices.traits import Trait from roborock.devices.transport.mqtt_channel import MqttChannel from .command import CommandTrait from .vacuum import VacuumTrait -_LOGGER = logging.getLogger(__name__) - __all__ = [ "Q10PropertiesApi", ] From fee3873330b7995706e69cb9a339bcbf34c24512 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:15:30 -0800 Subject: [PATCH 6/8] Fix test method name --- tests/devices/traits/b01/q10/test_vacuum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/devices/traits/b01/q10/test_vacuum.py b/tests/devices/traits/b01/q10/test_vacuum.py index 2bc6555f..394dc55a 100644 --- a/tests/devices/traits/b01/q10/test_vacuum.py +++ b/tests/devices/traits/b01/q10/test_vacuum.py @@ -34,7 +34,7 @@ def vacuumm_fixture(q10_api: Q10PropertiesApi) -> VacuumTrait: (lambda x: x.return_to_dock(), {"203": {}}), ], ) -async def test_q7_api_set_fan_speed( +async def test_vacuum_commands( vacuumm: VacuumTrait, fake_channel: FakeChannel, command_fn: Callable[[VacuumTrait], Awaitable[None]], From 68dbf43fce40d7fc256ab1308f790ded6f62f3c7 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Mon, 26 Jan 2026 21:16:01 -0800 Subject: [PATCH 7/8] Fix pydoc --- roborock/devices/traits/b01/q10/vacuum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roborock/devices/traits/b01/q10/vacuum.py b/roborock/devices/traits/b01/q10/vacuum.py index 8f6ecb23..0340ec09 100644 --- a/roborock/devices/traits/b01/q10/vacuum.py +++ b/roborock/devices/traits/b01/q10/vacuum.py @@ -13,7 +13,7 @@ class VacuumTrait: """ def __init__(self, command: CommandTrait) -> None: - """Initialize the CommandTrait.""" + """Initialize the VacuumTrait.""" self._command = command async def start_clean(self) -> None: From deef3ba29979899664c4115982276ab5620204a1 Mon Sep 17 00:00:00 2001 From: Luke Lashley Date: Tue, 27 Jan 2026 04:15:08 -0500 Subject: [PATCH 8/8] chore: wording Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- roborock/devices/traits/b01/q10/vacuum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roborock/devices/traits/b01/q10/vacuum.py b/roborock/devices/traits/b01/q10/vacuum.py index 0340ec09..1b7104c6 100644 --- a/roborock/devices/traits/b01/q10/vacuum.py +++ b/roborock/devices/traits/b01/q10/vacuum.py @@ -8,7 +8,7 @@ class VacuumTrait: """Trait for sending vacuum commands. - This is wrapper around the CommandTrait for sending vacuum related + This is a wrapper around the CommandTrait for sending vacuum related commands to Q10 devices. """