From d9dd756ce89bce9c657f138bb805f3af1413e189 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 14:12:40 +0200 Subject: [PATCH 01/17] store MotionSensitivity enum which makes communication to HA easier --- plugwise_usb/api.py | 2 +- plugwise_usb/nodes/scan.py | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 0350fdadb..f8cbd36f9 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -233,7 +233,7 @@ class MotionConfig: Attributes: reset_timer: int | None: Motion reset timer in minutes before the motion detection is switched off. daylight_mode: bool | None: Motion detection when light level is below threshold. - sensitivity_level: int | None: Motion sensitivity level. + sensitivity_level: MotionSensitivity | None: Motion sensitivity level. dirty: bool: Settings changed, device update pending """ diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index fc3e84c46..53188a6c9 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -198,7 +198,7 @@ def _reset_timer_from_cache(self) -> int | None: return int(reset_timer) return None - def _sensitivity_level_from_cache(self) -> int | None: + def _sensitivity_level_from_cache(self) -> MotionSensitivity | None: """Load sensitivity level from cache.""" if ( sensitivity_level := self._get_cache( @@ -326,14 +326,27 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: await self._scan_configure_update() return True - async def set_motion_sensitivity_level(self, level: int) -> bool: + async def set_motion_sensitivity_level(self, level: MotionSensitivity | int | str) -> bool: """Configure the motion sensitivity level.""" _LOGGER.debug( "set_motion_sensitivity_level | Device %s | %s -> %s", self.name, self._motion_config.sensitivity_level, - level, + str(level), ) + if isinstance(level,int): + try: + level = MotionSensitivity(level) + except ValueError: + _LOGGER.exception("MotionSensitivity for %s: value error ", self._mac_in_str) + return False + + if isinstance(level,str): + try: + level = MotionSensitivity[level] + except KeyError: + _LOGGER.exception("MotionSensitivity for %s: unknown level %s", self._mac_in_str,level) + return False if self._motion_config.sensitivity_level == level: return False self._motion_config = replace( @@ -447,7 +460,7 @@ async def scan_configure(self) -> bool: self._send, self._mac_in_bytes, self._motion_config.reset_timer, - self._motion_config.sensitivity_level, + self._motion_config.sensitivity_level.value, self._motion_config.daylight_mode, ) if (response := await request.send()) is None: @@ -478,7 +491,7 @@ async def _scan_configure_update(self) -> None: ) self._set_cache( CACHE_SCAN_CONFIG_SENSITIVITY, - str(MotionSensitivity(self._motion_config.sensitivity_level).name), + self._motion_config.sensitivity_level.name, ) self._set_cache( CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self._motion_config.daylight_mode) From 0f3603301e6f00c4857746de90b139c9823e3664 Mon Sep 17 00:00:00 2001 From: autoruff Date: Mon, 18 Aug 2025 12:14:53 +0000 Subject: [PATCH 02/17] fixup: mdi_scan Python code reformatted using Ruff --- plugwise_usb/nodes/scan.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 53188a6c9..aa9eb9a68 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -326,7 +326,9 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: await self._scan_configure_update() return True - async def set_motion_sensitivity_level(self, level: MotionSensitivity | int | str) -> bool: + async def set_motion_sensitivity_level( + self, level: MotionSensitivity | int | str + ) -> bool: """Configure the motion sensitivity level.""" _LOGGER.debug( "set_motion_sensitivity_level | Device %s | %s -> %s", @@ -334,18 +336,24 @@ async def set_motion_sensitivity_level(self, level: MotionSensitivity | int | st self._motion_config.sensitivity_level, str(level), ) - if isinstance(level,int): + if isinstance(level, int): try: level = MotionSensitivity(level) except ValueError: - _LOGGER.exception("MotionSensitivity for %s: value error ", self._mac_in_str) + _LOGGER.exception( + "MotionSensitivity for %s: value error ", self._mac_in_str + ) return False - - if isinstance(level,str): + + if isinstance(level, str): try: level = MotionSensitivity[level] except KeyError: - _LOGGER.exception("MotionSensitivity for %s: unknown level %s", self._mac_in_str,level) + _LOGGER.exception( + "MotionSensitivity for %s: unknown level %s", + self._mac_in_str, + level, + ) return False if self._motion_config.sensitivity_level == level: return False From 1df1bd50380fb3ce34ae9eaf38ba0207abf82aef Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 14:37:55 +0200 Subject: [PATCH 03/17] CR: Catched missing update --- plugwise_usb/api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index f8cbd36f9..191fcdbba 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -240,7 +240,7 @@ class MotionConfig: daylight_mode: bool | None = None reset_timer: int | None = None - sensitivity_level: int | None = None + sensitivity_level: MotionSensitivity | None = None dirty: bool = False @@ -659,7 +659,10 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: """ - async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: + async def set_motion_sensitivity_level( + self, + level: MotionSensitivity | int | str + ) -> bool: """Configure motion sensitivity level. Description: From 8ffa0c32f0f9a5c6a8529f04cf61151293991f21 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 16:28:20 +0200 Subject: [PATCH 04/17] reference to self-functions which return default values of config-object contains None --- plugwise_usb/nodes/scan.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index aa9eb9a68..ae5b75268 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -467,9 +467,9 @@ async def scan_configure(self) -> bool: request = ScanConfigureRequest( self._send, self._mac_in_bytes, - self._motion_config.reset_timer, - self._motion_config.sensitivity_level.value, - self._motion_config.daylight_mode, + self.reset_timer, + self.sensitivity_level.value, + self.daylight_mode, ) if (response := await request.send()) is None: _LOGGER.warning( @@ -495,16 +495,16 @@ async def scan_configure(self) -> bool: async def _scan_configure_update(self) -> None: """Push scan configuration update to cache.""" self._set_cache( - CACHE_SCAN_CONFIG_RESET_TIMER, str(self._motion_config.reset_timer) + CACHE_SCAN_CONFIG_RESET_TIMER, str(self.reset_timer) ) self._set_cache( CACHE_SCAN_CONFIG_SENSITIVITY, self._motion_config.sensitivity_level.name, ) self._set_cache( - CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self._motion_config.daylight_mode) + CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self.daylight_mode) ) - self._set_cache(CACHE_SCAN_CONFIG_DIRTY, str(self._motion_config.dirty)) + self._set_cache(CACHE_SCAN_CONFIG_DIRTY, str(self.dirty)) await gather( self.publish_feature_update_to_subscribers( NodeFeature.MOTION_CONFIG, From 91ead794313e814fe069a2b1e4eeafd599e0c80f Mon Sep 17 00:00:00 2001 From: autoruff Date: Mon, 18 Aug 2025 14:29:41 +0000 Subject: [PATCH 05/17] fixup: mdi_scan Python code reformatted using Ruff --- plugwise_usb/api.py | 3 +-- plugwise_usb/nodes/scan.py | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 191fcdbba..38d839266 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -660,8 +660,7 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: """ async def set_motion_sensitivity_level( - self, - level: MotionSensitivity | int | str + self, level: MotionSensitivity | int | str ) -> bool: """Configure motion sensitivity level. diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index ae5b75268..548694a6b 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -494,16 +494,12 @@ async def scan_configure(self) -> bool: async def _scan_configure_update(self) -> None: """Push scan configuration update to cache.""" - self._set_cache( - CACHE_SCAN_CONFIG_RESET_TIMER, str(self.reset_timer) - ) + self._set_cache(CACHE_SCAN_CONFIG_RESET_TIMER, str(self.reset_timer)) self._set_cache( CACHE_SCAN_CONFIG_SENSITIVITY, self._motion_config.sensitivity_level.name, ) - self._set_cache( - CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self.daylight_mode) - ) + self._set_cache(CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self.daylight_mode)) self._set_cache(CACHE_SCAN_CONFIG_DIRTY, str(self.dirty)) await gather( self.publish_feature_update_to_subscribers( From 68f3a3df84e476d6a6369d2efce6d77709117560 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 16:34:24 +0200 Subject: [PATCH 06/17] print wrong value of level in de log --- plugwise_usb/nodes/scan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 548694a6b..6c5b7140f 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -341,7 +341,9 @@ async def set_motion_sensitivity_level( level = MotionSensitivity(level) except ValueError: _LOGGER.exception( - "MotionSensitivity for %s: value error ", self._mac_in_str + "MotionSensitivity for %s: invalid numeric value %s", + self._mac_in_str, + str(level), ) return False From d29c9b73793b9abc4fee8027be2f91fcc3e2191a Mon Sep 17 00:00:00 2001 From: autoruff Date: Mon, 18 Aug 2025 14:35:17 +0000 Subject: [PATCH 07/17] fixup: mdi_scan Python code reformatted using Ruff --- plugwise_usb/nodes/scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 6c5b7140f..8c17551ce 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -341,7 +341,7 @@ async def set_motion_sensitivity_level( level = MotionSensitivity(level) except ValueError: _LOGGER.exception( - "MotionSensitivity for %s: invalid numeric value %s", + "MotionSensitivity for %s: invalid numeric value %s", self._mac_in_str, str(level), ) From 08911d47c35f37765023a4bbe22ecd93d7866d06 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 17:18:46 +0200 Subject: [PATCH 08/17] CR: update node.py as well --- plugwise_usb/nodes/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index 5c223ffa0..fd3622a65 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -787,7 +787,7 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: raise NotImplementedError() @raise_not_loaded - async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: + async def set_motion_sensitivity_level(self, level: MotionSensitivity |int | str) -> bool: """Configure motion sensitivity level.""" if NodeFeature.MOTION not in self._features: raise FeatureError( From 261d41a0e7d58017603f357f0ac2bd754ea05424 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 18 Aug 2025 17:21:35 +0200 Subject: [PATCH 09/17] release 0.44.12a2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6b79f94dd..1e6f29c52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "plugwise_usb" -version = "0.44.12a1" +version = "0.44.12a2" license = "MIT" keywords = ["home", "automation", "plugwise", "module", "usb"] classifiers = [ From c24d8da194ec449849e008b7b78e984eb339398c Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 19 Aug 2025 08:18:45 +0200 Subject: [PATCH 10/17] CR: Nitpick on formatting --- plugwise_usb/nodes/node.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index fd3622a65..c861da841 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -787,7 +787,9 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: raise NotImplementedError() @raise_not_loaded - async def set_motion_sensitivity_level(self, level: MotionSensitivity |int | str) -> bool: + async def set_motion_sensitivity_level( + self, level: MotionSensitivity | int | str + ) -> bool: """Configure motion sensitivity level.""" if NodeFeature.MOTION not in self._features: raise FeatureError( From 4a4252268f865e3e4a1ef7484aaf4acccd5db3e0 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 19 Aug 2025 17:35:26 +0200 Subject: [PATCH 11/17] expose scheduling of light calibration --- plugwise_usb/api.py | 7 +++++++ plugwise_usb/nodes/scan.py | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 38d839266..80ce33534 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -680,6 +680,13 @@ async def set_motion_sensitivity_level( """ + async def scan_calibrate_light(self) -> bool: + """Request to calibration light sensitivity of Scan device. + + Description: + Request to calibration light sensitivity of Scan device. + """ + async def set_relay_init(self, state: bool) -> bool: """Change the initial state of the relay. diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 8c17551ce..20123f5ef 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -86,7 +86,7 @@ def __init__( self._motion_state = MotionState() self._motion_config = MotionConfig() - + self._scan_calibrate_light_scheduled: bool = False self._configure_daylight_mode_task: Task[Coroutine[Any, Any, None]] | None = ( None ) @@ -449,6 +449,8 @@ async def _run_awake_tasks(self) -> None: await super()._run_awake_tasks() if self._motion_config.dirty: await self._configure_scan_task() + if self._scan_calibrate_light_scheduled: + await self._scan_calibrate_light() await self.publish_feature_update_to_subscribers( NodeFeature.MOTION_CONFIG, self._motion_config, @@ -511,7 +513,11 @@ async def _scan_configure_update(self) -> None: self.save_cache(), ) - async def scan_calibrate_light(self) -> bool: + async def scan_calibrate_light(self) -> None: + """Schedule light sensitivity calibration of Scan device.""" + self._scan_calibrate_light_scheduled = True + + async def _scan_calibrate_light(self) -> bool: """Request to calibration light sensitivity of Scan device.""" request = ScanLightCalibrateRequest(self._send, self._mac_in_bytes) if (response := await request.send()) is not None: @@ -519,6 +525,7 @@ async def scan_calibrate_light(self) -> bool: response.node_ack_type == NodeAckResponseType.SCAN_LIGHT_CALIBRATION_ACCEPTED ): + self._scan_calibrate_light_scheduled = False return True return False raise NodeTimeout( From 99dc860bd2f3fdb67fb45ddb93b4dd463122769f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 19:15:34 +0200 Subject: [PATCH 12/17] Revert api/set_motion_sensitivity_level() typing --- plugwise_usb/api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 80ce33534..97873d328 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -659,9 +659,7 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: """ - async def set_motion_sensitivity_level( - self, level: MotionSensitivity | int | str - ) -> bool: + async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: """Configure motion sensitivity level. Description: From 663aba7a5db8750243b58e1aa5b5f9ae20a06c7a Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 19:17:50 +0200 Subject: [PATCH 13/17] Revert node/set_motion_sensitivity_level() typing --- plugwise_usb/nodes/node.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index c861da841..5c223ffa0 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -787,9 +787,7 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: raise NotImplementedError() @raise_not_loaded - async def set_motion_sensitivity_level( - self, level: MotionSensitivity | int | str - ) -> bool: + async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: """Configure motion sensitivity level.""" if NodeFeature.MOTION not in self._features: raise FeatureError( From 4b4a14ca35966567476bb78e635f93eabcee0373 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 19:43:02 +0200 Subject: [PATCH 14/17] Assure proper input-output --- plugwise_usb/nodes/scan.py | 39 ++++++++------------------------------ 1 file changed, 8 insertions(+), 31 deletions(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 20123f5ef..bc24eb7f7 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -86,7 +86,7 @@ def __init__( self._motion_state = MotionState() self._motion_config = MotionConfig() - self._scan_calibrate_light_scheduled: bool = False + self._scan_calibrate_light_scheduled = False self._configure_daylight_mode_task: Task[Coroutine[Any, Any, None]] | None = ( None ) @@ -165,7 +165,7 @@ async def _load_from_cache(self) -> bool: self._motion_config = MotionConfig( daylight_mode=daylight_mode, reset_timer=reset_timer, - sensitivity_level=sensitivity_level, + sensitivity_level=sensitivity_level.value, dirty=dirty, ) if dirty: @@ -326,42 +326,19 @@ async def set_motion_reset_timer(self, minutes: int) -> bool: await self._scan_configure_update() return True - async def set_motion_sensitivity_level( - self, level: MotionSensitivity | int | str - ) -> bool: + async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: """Configure the motion sensitivity level.""" _LOGGER.debug( "set_motion_sensitivity_level | Device %s | %s -> %s", self.name, self._motion_config.sensitivity_level, - str(level), + level.value, ) - if isinstance(level, int): - try: - level = MotionSensitivity(level) - except ValueError: - _LOGGER.exception( - "MotionSensitivity for %s: invalid numeric value %s", - self._mac_in_str, - str(level), - ) - return False - - if isinstance(level, str): - try: - level = MotionSensitivity[level] - except KeyError: - _LOGGER.exception( - "MotionSensitivity for %s: unknown level %s", - self._mac_in_str, - level, - ) - return False - if self._motion_config.sensitivity_level == level: + if self._motion_config.sensitivity_level == level.value: return False self._motion_config = replace( self._motion_config, - sensitivity_level=level, + sensitivity_level=level.value, dirty=True, ) await self._scan_configure_update() @@ -472,7 +449,7 @@ async def scan_configure(self) -> bool: self._send, self._mac_in_bytes, self.reset_timer, - self.sensitivity_level.value, + self.sensitivity_level, self.daylight_mode, ) if (response := await request.send()) is None: @@ -501,7 +478,7 @@ async def _scan_configure_update(self) -> None: self._set_cache(CACHE_SCAN_CONFIG_RESET_TIMER, str(self.reset_timer)) self._set_cache( CACHE_SCAN_CONFIG_SENSITIVITY, - self._motion_config.sensitivity_level.name, + self._motion_config.sensitivity_level, ) self._set_cache(CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self.daylight_mode)) self._set_cache(CACHE_SCAN_CONFIG_DIRTY, str(self.dirty)) From 8d7ba2ca5c05e63de793dff44dee7030809755af Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 19:49:07 +0200 Subject: [PATCH 15/17] Fix set_motion_sensitivity_level tests --- tests/test_usb.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 2c0917bc6..f76883cb3 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1834,7 +1834,7 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign await test_node.set_motion_daylight_mode(True) with pytest.raises(pw_exceptions.NodeError): - await test_node.set_motion_sensitivity_level(20) + await test_node.set_motion_sensitivity_level(pw_api.MotionSensitivity.HIGH) with pytest.raises(pw_exceptions.NodeError): await test_node.set_motion_reset_timer(5) @@ -1865,7 +1865,7 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign await test_node.set_motion_daylight_mode(True) with pytest.raises(pw_exceptions.FeatureError): - await test_node.set_motion_sensitivity_level(20) + await test_node.set_motion_sensitivity_level(pw_api.MotionSensitivity.HIGH) with pytest.raises(pw_exceptions.FeatureError): await test_node.set_motion_reset_timer(5) @@ -1892,7 +1892,7 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign with pytest.raises(NotImplementedError): await test_node.set_motion_daylight_mode(True) with pytest.raises(NotImplementedError): - await test_node.set_motion_sensitivity_level(20) + await test_node.set_motion_sensitivity_level(pw_api.MotionSensitivity.HIGH) with pytest.raises(NotImplementedError): await test_node.set_motion_reset_timer(5) @@ -2242,10 +2242,14 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign # test motion sensitivity level assert test_scan.sensitivity_level == 30 assert test_scan.motion_config.sensitivity_level == 30 - assert not await test_scan.set_motion_sensitivity_level(30) + assert not await test_scan.set_motion_sensitivity_level( + pw_api.MotionSensitivity.MEDIUM + ) assert not test_scan.motion_config.dirty - assert await test_scan.set_motion_sensitivity_level(20) + assert await test_scan.set_motion_sensitivity_level( + pw_api.MotionSensitivity.HIGH + ) assert test_scan.motion_config.dirty awake_response4 = pw_responses.NodeAwakeResponse() awake_response4.deserialize( From a78207d873e2d41f8b4ad457f86f7ce494132739 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 19:57:53 +0200 Subject: [PATCH 16/17] Fix sensitivity_level property --- plugwise_usb/nodes/scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index bc24eb7f7..74ebb7fd4 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -278,7 +278,7 @@ def sensitivity_level(self) -> int: """Sensitivity level of motion sensor.""" if self._motion_config.sensitivity_level is not None: return self._motion_config.sensitivity_level - return DEFAULT_SENSITIVITY + return DEFAULT_SENSITIVITY.value # endregion # region Configuration actions From 8072bd35564d3423e0d8a06fb3909b85aac29717 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Fri, 22 Aug 2025 20:19:31 +0200 Subject: [PATCH 17/17] Scan: MotionSensitivity all the way --- plugwise_usb/messages/requests.py | 5 +++-- plugwise_usb/nodes/scan.py | 16 ++++++++-------- tests/test_usb.py | 14 +++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/plugwise_usb/messages/requests.py b/plugwise_usb/messages/requests.py index 39e07a46b..4e6dc2b36 100644 --- a/plugwise_usb/messages/requests.py +++ b/plugwise_usb/messages/requests.py @@ -9,6 +9,7 @@ import logging from typing import Any +from ..api import MotionSensitivity from ..constants import ( DAY_IN_MINUTES, HOUR_IN_MINUTES, @@ -1376,14 +1377,14 @@ def __init__( send_fn: Callable[[PlugwiseRequest, bool], Awaitable[PlugwiseResponse | None]], mac: bytes, reset_timer: int, - sensitivity: int, + sensitivity: MotionSensitivity, light: bool, ): """Initialize ScanConfigureRequest message object.""" super().__init__(send_fn, mac) reset_timer_value = Int(reset_timer, length=2) # Sensitivity: HIGH(0x14), MEDIUM(0x1E), OFF(0xFF) - sensitivity_value = Int(sensitivity, length=2) + sensitivity_value = Int(sensitivity.value, length=2) light_temp = 1 if light else 0 light_value = Int(light_temp, length=2) self._args += [ diff --git a/plugwise_usb/nodes/scan.py b/plugwise_usb/nodes/scan.py index 74ebb7fd4..59cee3dde 100644 --- a/plugwise_usb/nodes/scan.py +++ b/plugwise_usb/nodes/scan.py @@ -165,7 +165,7 @@ async def _load_from_cache(self) -> bool: self._motion_config = MotionConfig( daylight_mode=daylight_mode, reset_timer=reset_timer, - sensitivity_level=sensitivity_level.value, + sensitivity_level=sensitivity_level, dirty=dirty, ) if dirty: @@ -274,11 +274,11 @@ def reset_timer(self) -> int: return DEFAULT_RESET_TIMER @property - def sensitivity_level(self) -> int: + def sensitivity_level(self) -> MotionSensitivity: """Sensitivity level of motion sensor.""" if self._motion_config.sensitivity_level is not None: return self._motion_config.sensitivity_level - return DEFAULT_SENSITIVITY.value + return DEFAULT_SENSITIVITY # endregion # region Configuration actions @@ -331,14 +331,14 @@ async def set_motion_sensitivity_level(self, level: MotionSensitivity) -> bool: _LOGGER.debug( "set_motion_sensitivity_level | Device %s | %s -> %s", self.name, - self._motion_config.sensitivity_level, - level.value, + self._motion_config.sensitivity_level.name, + level.name, ) - if self._motion_config.sensitivity_level == level.value: + if self._motion_config.sensitivity_level == level: return False self._motion_config = replace( self._motion_config, - sensitivity_level=level.value, + sensitivity_level=level, dirty=True, ) await self._scan_configure_update() @@ -478,7 +478,7 @@ async def _scan_configure_update(self) -> None: self._set_cache(CACHE_SCAN_CONFIG_RESET_TIMER, str(self.reset_timer)) self._set_cache( CACHE_SCAN_CONFIG_SENSITIVITY, - self._motion_config.sensitivity_level, + self._motion_config.sensitivity_level.name, ) self._set_cache(CACHE_SCAN_CONFIG_DAYLIGHT_MODE, str(self.daylight_mode)) self._set_cache(CACHE_SCAN_CONFIG_DIRTY, str(self.dirty)) diff --git a/tests/test_usb.py b/tests/test_usb.py index f76883cb3..7b20edc5e 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -1494,7 +1494,7 @@ async def test_creating_request_messages(self) -> None: self.dummy_fn, b"1111222233334444", 5, # Delay in minutes when signal is send when no motion is detected - 30, # Sensitivity of Motion sensor (High, Medium, Off) + pw_api.MotionSensitivity.MEDIUM, # Sensitivity of Motion sensor (High, Medium, Off) False, # Daylight override to only report motion when lightlevel is below calibrated level ) assert ( @@ -2240,8 +2240,10 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign assert test_scan.motion_config.daylight_mode # test motion sensitivity level - assert test_scan.sensitivity_level == 30 - assert test_scan.motion_config.sensitivity_level == 30 + assert test_scan.sensitivity_level == pw_api.MotionSensitivity.MEDIUM + assert ( + test_scan.motion_config.sensitivity_level == pw_api.MotionSensitivity.MEDIUM + ) assert not await test_scan.set_motion_sensitivity_level( pw_api.MotionSensitivity.MEDIUM ) @@ -2261,8 +2263,10 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign await test_scan._awake_response(awake_response4) # pylint: disable=protected-access await asyncio.sleep(0.001) # Ensure time for task to be executed assert not test_scan.motion_config.dirty - assert test_scan.sensitivity_level == 20 - assert test_scan.motion_config.sensitivity_level == 20 + assert test_scan.sensitivity_level == pw_api.MotionSensitivity.HIGH + assert ( + test_scan.motion_config.sensitivity_level == pw_api.MotionSensitivity.HIGH + ) # scan with cache enabled mock_stick_controller.send_response = None