From b68bee841ec97cc7c9f42f5033adcce0db4e4f7d Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Wed, 13 Aug 2025 12:48:14 +0200 Subject: [PATCH 1/6] properly propagate configuration changes --- plugwise_usb/nodes/scan.py | 5 +++++ plugwise_usb/nodes/sed.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index f97ddc26a..6650a6266 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -170,6 +170,11 @@ async def _load_from_cache(self) -> bool: ) if dirty: await self._scan_configure_update() + else: + await self.publish_feature_update_to_subscribers( + NodeFeature.MOTION_CONFIG, + self._motion_config, + ) return super_load_success def _daylight_mode_from_cache(self) -> bool | None: diff --git a/plugwise_usb/nodes/sed.py b/plugwise_usb/nodes/sed.py index aab5615ae..9d7ee103e 100644 --- a/plugwise_usb/nodes/sed.py +++ b/plugwise_usb/nodes/sed.py @@ -175,6 +175,11 @@ async def _load_from_cache(self) -> bool: ) if dirty: await self._sed_configure_update() + else: + await self.publish_feature_update_to_subscribers( + NodeFeature.BATTERY, + self._battery_config, + ) self._awake_timestamp_from_cache() self._awake_reason_from_cache() return super_load_success @@ -243,6 +248,7 @@ async def set_awake_duration(self, seconds: int) -> bool: awake_duration=seconds, dirty=True, ) + await self._sed_configure_update() return True async def set_clock_interval(self, minutes: int) -> bool: @@ -264,6 +270,7 @@ async def set_clock_interval(self, minutes: int) -> bool: self._battery_config = replace( self._battery_config, clock_interval=minutes, dirty=True ) + await self._sed_configure_update() return True async def set_clock_sync(self, sync: bool) -> bool: @@ -280,6 +287,7 @@ async def set_clock_sync(self, sync: bool) -> bool: self._battery_config = replace( self._battery_config, clock_sync=sync, dirty=True ) + await self._sed_configure_update() return True async def set_maintenance_interval(self, minutes: int) -> bool: @@ -301,6 +309,7 @@ async def set_maintenance_interval(self, minutes: int) -> bool: self._battery_config = replace( self._battery_config, maintenance_interval=minutes, dirty=True ) + await self._sed_configure_update() return True async def set_sleep_duration(self, minutes: int) -> bool: @@ -325,6 +334,7 @@ async def set_sleep_duration(self, minutes: int) -> bool: self._battery_config = replace( self._battery_config, sleep_duration=minutes, dirty=True ) + await self._sed_configure_update() return True # endregion From 05454f6e33931cc818abb5891ddde4ade619d71d Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Wed, 13 Aug 2025 13:38:40 +0200 Subject: [PATCH 2/6] CR: Add check on parameter not changed awake_duration --- plugwise_usb/nodes/sed.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/nodes/sed.py b/plugwise_usb/nodes/sed.py index 9d7ee103e..4afdd5e8d 100644 --- a/plugwise_usb/nodes/sed.py +++ b/plugwise_usb/nodes/sed.py @@ -242,6 +242,8 @@ async def set_awake_duration(self, seconds: int) -> bool: raise ValueError( f"Invalid awake duration ({seconds}). It must be between 1 and 255 seconds." ) + if self._battery_config.awake_duration == seconds: + return False self._battery_config = replace( self._battery_config, From c64cb6af8618a590ac7041b769d6dcb42390c5f9 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Wed, 13 Aug 2025 14:28:01 +0200 Subject: [PATCH 3/6] fix test --- tests/test_usb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index cfef1353e..2c0917bc6 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -2000,14 +2000,14 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign awake_response2.timestamp = awake_response1.timestamp + td( seconds=pw_sed.AWAKE_RETRY ) - assert await test_sed.set_awake_duration(15) + assert await test_sed.set_awake_duration(20) assert test_sed.battery_config.dirty mock_stick_controller.send_response = sed_config_accepted await test_sed._awake_response(awake_response2) # pylint: disable=protected-access await asyncio.sleep(0.001) # Ensure time for task to be executed assert not test_sed.battery_config.dirty - assert test_sed.battery_config.awake_duration == 15 - assert test_sed.awake_duration == 15 + assert test_sed.battery_config.awake_duration == 20 + assert test_sed.awake_duration == 20 # test maintenance interval assert test_sed.maintenance_interval == 60 From d937a30bc425057d1fd1dfa7a974368bfa3cfd37 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Sat, 16 Aug 2025 10:01:42 +0200 Subject: [PATCH 4/6] fix publishing of NodeFeature.MOTION_CONFIG and revert change on NodeFeature.BATTERY --- plugwise_usb/nodes/scan.py | 11 +++++------ plugwise_usb/nodes/sed.py | 5 ----- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 6650a6266..bbf221d6a 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -102,8 +102,8 @@ async def load(self) -> None: await super().load() self._setup_protocol(SCAN_FIRMWARE_SUPPORT, SCAN_FEATURES) - await self.initialize() await self._loaded_callback(NodeEvent.LOADED, self.mac) + await self.initialize() async def initialize(self) -> None: """Initialize Scan node.""" @@ -170,11 +170,6 @@ async def _load_from_cache(self) -> bool: ) if dirty: await self._scan_configure_update() - else: - await self.publish_feature_update_to_subscribers( - NodeFeature.MOTION_CONFIG, - self._motion_config, - ) return super_load_success def _daylight_mode_from_cache(self) -> bool | None: @@ -431,6 +426,10 @@ async def _run_awake_tasks(self) -> None: await super()._run_awake_tasks() if self._motion_config.dirty: await self._configure_scan_task() + await self.publish_feature_update_to_subscribers( + NodeFeature.MOTION_CONFIG, + self._motion_config, + ) async def _configure_scan_task(self) -> bool: """Configure Scan device settings. Returns True if successful.""" diff --git a/plugwise_usb/nodes/sed.py b/plugwise_usb/nodes/sed.py index 4afdd5e8d..acf3b81cc 100644 --- a/plugwise_usb/nodes/sed.py +++ b/plugwise_usb/nodes/sed.py @@ -175,11 +175,6 @@ async def _load_from_cache(self) -> bool: ) if dirty: await self._sed_configure_update() - else: - await self.publish_feature_update_to_subscribers( - NodeFeature.BATTERY, - self._battery_config, - ) self._awake_timestamp_from_cache() self._awake_reason_from_cache() return super_load_success From 1345e0c5386b6733d8fa0d87a506078cf382aea4 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Sat, 16 Aug 2025 13:25:00 +0200 Subject: [PATCH 5/6] ruff scan --- plugwise_usb/nodes/scan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index bbf221d6a..fc3e84c46 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -427,9 +427,9 @@ async def _run_awake_tasks(self) -> None: if self._motion_config.dirty: await self._configure_scan_task() await self.publish_feature_update_to_subscribers( - NodeFeature.MOTION_CONFIG, - self._motion_config, - ) + NodeFeature.MOTION_CONFIG, + self._motion_config, + ) async def _configure_scan_task(self) -> bool: """Configure Scan device settings. Returns True if successful.""" From dcb8909301ad2675b3f10b14973d7ec150a505c2 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Sat, 16 Aug 2025 13:36:40 +0200 Subject: [PATCH 6/6] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90190a3a9..e11edc4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Ongoing - PR [319](https://github.com/plugwise/python-plugwise-usb/pull/319): Replace unclear warning message when a node is not online, also various small improvements suggested by CRAI. +- PR [312](https://github.com/plugwise/python-plugwise-usb/pull/312): properly propagate configuration changes and initialize to available on first node wakeup ## v0.44.11 - 2025-08-14