From 1c765cb4eed3052186c70300f2e73bfaa50ac378 Mon Sep 17 00:00:00 2001 From: Thomas Rupprecht Date: Wed, 3 Jun 2026 23:58:30 +0200 Subject: [PATCH 1/2] feat(q7): add set_do_not_disturb trait method Co-Authored-By: Claude Opus 4.8 (1M context) --- roborock/devices/traits/b01/q7/__init__.py | 16 ++++++++++++++++ tests/devices/traits/b01/q7/test_init.py | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/roborock/devices/traits/b01/q7/__init__.py b/roborock/devices/traits/b01/q7/__init__.py index 13de90fa..aaf1f782 100644 --- a/roborock/devices/traits/b01/q7/__init__.py +++ b/roborock/devices/traits/b01/q7/__init__.py @@ -116,6 +116,22 @@ 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_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None: + """Configure do-not-disturb. + + The device expects all three values together via ``service.set_quiet_time`` + (individual ``prop.set`` calls are ignored). ``begin_time``/``end_time`` are + minutes since midnight. + """ + await self.send( + RoborockB01Q7Methods.SET_QUIET_TIME, + { + "is_open": int(enabled), + "quiet_begin_time": begin_time, + "quiet_end_time": end_time, + }, + ) + async def start_clean(self) -> None: """Start cleaning.""" await self.send( diff --git a/tests/devices/traits/b01/q7/test_init.py b/tests/devices/traits/b01/q7/test_init.py index 673213cc..c3071817 100644 --- a/tests/devices/traits/b01/q7/test_init.py +++ b/tests/devices/traits/b01/q7/test_init.py @@ -192,6 +192,28 @@ async def test_q7_api_set_child_lock( assert payload_data["dps"]["10000"]["params"] == {RoborockB01Props.CHILD_LOCK: expected_code} +@pytest.mark.parametrize("enabled, expected_is_open", [(True, 1), (False, 0)]) +async def test_q7_api_set_do_not_disturb( + enabled: bool, + expected_is_open: int, + q7_api: Q7PropertiesApi, + fake_channel: FakeChannel, + message_builder: B01MessageBuilder, +): + """Test do-not-disturb is set as a whole via service.set_quiet_time.""" + fake_channel.response_queue.append(message_builder.build({"result": "ok"})) + await q7_api.set_do_not_disturb(enabled, 1200, 420) + + message = fake_channel.published_messages[0] + payload_data = json.loads(unpad(message.payload, AES.block_size)) + assert payload_data["dps"]["10000"]["method"] == "service.set_quiet_time" + assert payload_data["dps"]["10000"]["params"] == { + "is_open": expected_is_open, + "quiet_begin_time": 1200, + "quiet_end_time": 420, + } + + @pytest.mark.parametrize( ("mode", "expected_code"), [ From 402d2a5678f1c0a2061aff53fa633e1841e16c3b Mon Sep 17 00:00:00 2001 From: Thomas Rupprecht Date: Sun, 21 Jun 2026 17:49:20 +0200 Subject: [PATCH 2/2] fix(q7): validate do-not-disturb time range Validate begin_time/end_time are within 0-1439 minutes since midnight and raise a clear ValueError when out of bounds. Clarify in the docstring that cross-midnight ranges are supported via begin_time > end_time. Co-Authored-By: Claude Opus 4.8 (1M context) --- roborock/devices/traits/b01/q7/__init__.py | 9 ++++++++- tests/devices/traits/b01/q7/test_init.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/roborock/devices/traits/b01/q7/__init__.py b/roborock/devices/traits/b01/q7/__init__.py index aaf1f782..a48dc344 100644 --- a/roborock/devices/traits/b01/q7/__init__.py +++ b/roborock/devices/traits/b01/q7/__init__.py @@ -121,8 +121,15 @@ async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int The device expects all three values together via ``service.set_quiet_time`` (individual ``prop.set`` calls are ignored). ``begin_time``/``end_time`` are - minutes since midnight. + minutes since midnight and must be in the range 0-1439 (inclusive). + + Ranges that cross midnight are supported by passing a ``begin_time`` that is + greater than ``end_time`` (e.g. 22:00-07:00 is ``begin_time=1320``, + ``end_time=420``). """ + for name, value in (("begin_time", begin_time), ("end_time", end_time)): + if not 0 <= value <= 1439: + raise ValueError(f"{name} must be between 0 and 1439 minutes since midnight, got {value}") await self.send( RoborockB01Q7Methods.SET_QUIET_TIME, { diff --git a/tests/devices/traits/b01/q7/test_init.py b/tests/devices/traits/b01/q7/test_init.py index c3071817..0c04a261 100644 --- a/tests/devices/traits/b01/q7/test_init.py +++ b/tests/devices/traits/b01/q7/test_init.py @@ -214,6 +214,23 @@ async def test_q7_api_set_do_not_disturb( } +@pytest.mark.parametrize( + ("begin_time", "end_time"), + [(-1, 420), (1440, 420), (1200, -1), (1200, 1440)], +) +async def test_q7_api_set_do_not_disturb_invalid_time( + begin_time: int, + end_time: int, + q7_api: Q7PropertiesApi, + fake_channel: FakeChannel, +): + """Test out-of-range times raise ValueError and nothing is sent.""" + with pytest.raises(ValueError, match="minutes since midnight"): + await q7_api.set_do_not_disturb(True, begin_time, end_time) + + assert len(fake_channel.published_messages) == 0 + + @pytest.mark.parametrize( ("mode", "expected_code"), [