From 9f372d231c563b5eb776baa9ea220106ac34f185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sat, 21 Feb 2026 10:13:04 +0100 Subject: [PATCH 1/3] feat(q10): add status update listener API --- roborock/devices/traits/b01/q10/status.py | 25 ++++++++++++++++++--- tests/devices/traits/b01/q10/test_status.py | 20 +++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/roborock/devices/traits/b01/q10/status.py b/roborock/devices/traits/b01/q10/status.py index 7f44a526..97a731e5 100644 --- a/roborock/devices/traits/b01/q10/status.py +++ b/roborock/devices/traits/b01/q10/status.py @@ -1,7 +1,11 @@ """Status trait for Q10 B01 devices.""" import logging +from collections.abc import Callable +from typing import Any +from roborock.callbacks import CallbackList +from roborock.data.b01_q10.b01_q10_code_mappings import B01_Q10_DP from roborock.data.b01_q10.b01_q10_containers import Q10Status from .common import DpsDataConverter @@ -19,8 +23,23 @@ class StatusTrait(Q10Status): New values can be requited through the `Q10PropertiesApi`'s `refresh` method. """ - def update_from_dps(self, decoded_dps: dict) -> None: + def __init__(self) -> None: + """Initialize the status trait.""" + super().__init__() + self._update_callbacks: CallbackList[dict[B01_Q10_DP, Any]] = CallbackList( + logger=_LOGGER + ) + + def add_update_listener( + self, callback: Callable[[dict[B01_Q10_DP, Any]], None] + ) -> Callable[[], None]: + """Register a callback for decoded DPS updates. + + Returns a callable to remove the listener. + """ + return self._update_callbacks.add_callback(callback) + + def update_from_dps(self, decoded_dps: dict[B01_Q10_DP, Any]) -> None: """Update the trait from raw DPS data.""" _CONVERTER.update_from_dps(self, decoded_dps) - # In the future we can register listeners and notify them here on update - # if `update_from_dps` performed any updates. + self._update_callbacks(decoded_dps) diff --git a/tests/devices/traits/b01/q10/test_status.py b/tests/devices/traits/b01/q10/test_status.py index b2c56ee9..06f5b196 100644 --- a/tests/devices/traits/b01/q10/test_status.py +++ b/tests/devices/traits/b01/q10/test_status.py @@ -10,6 +10,7 @@ import pytest from roborock.data.b01_q10.b01_q10_code_mappings import ( + B01_Q10_DP, YXDeviceCleanTask, YXDeviceState, YXFanLevel, @@ -139,3 +140,22 @@ async def test_status_trait_refresh( assert q10_api.status.battery == 100 assert q10_api.status.status == YXDeviceState.CHARGING_STATE assert q10_api.status.fan_level == YXFanLevel.NORMAL + + +def test_status_trait_update_listener(q10_api: Q10PropertiesApi) -> None: + """Test that status listeners receive updates and can unsubscribe.""" + updates: list[dict[B01_Q10_DP, Any]] = [] + + unsubscribe = q10_api.status.add_update_listener(updates.append) + + first_update = {B01_Q10_DP.BATTERY: 88} + q10_api.status.update_from_dps(first_update) + + assert updates == [first_update] + + unsubscribe() + + second_update = {B01_Q10_DP.BATTERY: 87} + q10_api.status.update_from_dps(second_update) + + assert updates == [first_update] From 5ca5b6ebc65d23529e69ed5cbb7e696ac62ae33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sat, 21 Feb 2026 10:15:18 +0100 Subject: [PATCH 2/3] style(q10): apply ruff-format to status trait --- roborock/devices/traits/b01/q10/status.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/roborock/devices/traits/b01/q10/status.py b/roborock/devices/traits/b01/q10/status.py index 97a731e5..80aa8741 100644 --- a/roborock/devices/traits/b01/q10/status.py +++ b/roborock/devices/traits/b01/q10/status.py @@ -26,13 +26,9 @@ class StatusTrait(Q10Status): def __init__(self) -> None: """Initialize the status trait.""" super().__init__() - self._update_callbacks: CallbackList[dict[B01_Q10_DP, Any]] = CallbackList( - logger=_LOGGER - ) + self._update_callbacks: CallbackList[dict[B01_Q10_DP, Any]] = CallbackList(logger=_LOGGER) - def add_update_listener( - self, callback: Callable[[dict[B01_Q10_DP, Any]], None] - ) -> Callable[[], None]: + def add_update_listener(self, callback: Callable[[dict[B01_Q10_DP, Any]], None]) -> Callable[[], None]: """Register a callback for decoded DPS updates. Returns a callable to remove the listener. From 62bae8495c56d0462a9cb055c4ff753114389663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Sat, 21 Feb 2026 10:20:05 +0100 Subject: [PATCH 3/3] fix(q10): correct typo in docstring for StatusTrait --- roborock/devices/traits/b01/q10/status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roborock/devices/traits/b01/q10/status.py b/roborock/devices/traits/b01/q10/status.py index 80aa8741..329ff104 100644 --- a/roborock/devices/traits/b01/q10/status.py +++ b/roborock/devices/traits/b01/q10/status.py @@ -20,7 +20,7 @@ class StatusTrait(Q10Status): This is a thin wrapper around Q10Status that provides the Trait interface. The current values reflect the most recently received data from the device. - New values can be requited through the `Q10PropertiesApi`'s `refresh` method. + New values can be requested through the `Q10PropertiesApi`'s `refresh` method. """ def __init__(self) -> None: