From d4fbc1507ce3e113262736683f47dff09213b44f Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 15 Aug 2026 13:19:19 -0400 Subject: [PATCH] feat: add dust collection settings to the B01 Q7 api Adds set_dust_collection (auto-empty on/off) and set_dust_collection_frequency (cleans per emptying) to Q7PropertiesApi, using prop.set like the other Q7 setters. Both verified against a real Q7 M5+ (roborock.vacuum.sc05, fw 03.01.80): dust_auto_state and dust_frequency round-trip correctly. Note that writing dust_action (empty now) is rejected by the device with code 1, so a manual-empty trigger is intentionally not included; it likely requires the dock-task DPS (203 start_dock_task) instead. --- roborock/devices/traits/b01/q7/__init__.py | 10 +++++ tests/devices/traits/b01/q7/test_init.py | 50 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/roborock/devices/traits/b01/q7/__init__.py b/roborock/devices/traits/b01/q7/__init__.py index 35a29144..88ee18a7 100644 --- a/roborock/devices/traits/b01/q7/__init__.py +++ b/roborock/devices/traits/b01/q7/__init__.py @@ -117,6 +117,16 @@ async def set_child_lock(self, enabled: bool) -> None: """Enable or disable the child lock.""" await self.set_prop(RoborockB01Props.CHILD_LOCK, int(enabled)) + async def set_dust_collection(self, enabled: bool) -> None: + """Enable or disable automatic dust collection at the dock.""" + await self.set_prop(RoborockB01Props.DUST_AUTO_STATE, int(enabled)) + + async def set_dust_collection_frequency(self, frequency: int) -> None: + """Set how often the dock auto-empties, in cleans per emptying (1 = every clean).""" + if frequency < 1: + raise ValueError(f"frequency must be a positive number of cleans, got {frequency}") + await self.set_prop(RoborockB01Props.DUST_FREQUENCY, frequency) + async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None: """Configure do-not-disturb. diff --git a/tests/devices/traits/b01/q7/test_init.py b/tests/devices/traits/b01/q7/test_init.py index c273d2d4..7707bbe6 100644 --- a/tests/devices/traits/b01/q7/test_init.py +++ b/tests/devices/traits/b01/q7/test_init.py @@ -133,6 +133,56 @@ async def test_q7_api_set_child_lock( assert params == {RoborockB01Props.CHILD_LOCK: expected_code} +@pytest.mark.parametrize( + ("enabled", "expected_code"), + [(True, 1), (False, 0)], +) +async def test_q7_api_set_dust_collection( + enabled: bool, + expected_code: int, + q7_api: Q7PropertiesApi, + fake_channel: FakeQ7Channel, +): + """Test toggling automatic dust collection.""" + fake_channel.response_queue.append({"result": "ok"}) + await q7_api.set_dust_collection(enabled) + + assert len(fake_channel.published_commands) == 1 + command, params = fake_channel.published_commands[0] + assert command == RoborockB01Q7Methods.SET_PROP + assert params == {RoborockB01Props.DUST_AUTO_STATE: expected_code} + + +@pytest.mark.parametrize("frequency", [1, 2, 3]) +async def test_q7_api_set_dust_collection_frequency( + frequency: int, + q7_api: Q7PropertiesApi, + fake_channel: FakeQ7Channel, +): + """Test setting the automatic dust-collection frequency.""" + fake_channel.response_queue.append({"result": "ok"}) + await q7_api.set_dust_collection_frequency(frequency) + + assert len(fake_channel.published_commands) == 1 + command, params = fake_channel.published_commands[0] + assert command == RoborockB01Q7Methods.SET_PROP + assert params == {RoborockB01Props.DUST_FREQUENCY: frequency} + + +@pytest.mark.parametrize("frequency", [0, -1]) +async def test_q7_api_set_dust_collection_frequency_invalid( + frequency: int, + q7_api: Q7PropertiesApi, + fake_channel: FakeQ7Channel, +): + """Test invalid dust-collection frequencies raise without publishing.""" + fake_channel.response_queue.append({"result": "ok"}) + with pytest.raises(ValueError, match="positive number of cleans"): + await q7_api.set_dust_collection_frequency(frequency) + + assert len(fake_channel.published_commands) == 0 + + @pytest.mark.parametrize("enabled, expected_is_open", [(True, 1), (False, 0)]) async def test_q7_api_set_do_not_disturb( enabled: bool,