From 45eadb452ba450d1b05f5b35755e19d27530eac1 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 9 Jun 2025 19:35:21 +0200 Subject: [PATCH 01/16] Update sense parameter handling to resemble the switch/scan api Update sense initialisation logic matching how the switch works. --- plugwise_usb/api.py | 11 +++++++ plugwise_usb/nodes/node.py | 22 ++++++++++++++ plugwise_usb/nodes/sense.py | 57 +++++++++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 472d06ac0..3ca553eee 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -229,6 +229,17 @@ class EnergyStatistics: day_production: float | None = None day_production_reset: datetime | None = None +@dataclass +class Temperature: + """Temperature statistics collection.""" + + temperature: float | None = None + +@dataclass +class Humidity: + """Humidity statistics collection.""" + + humidity: float | None = None class PlugwiseNode(Protocol): """Protocol definition of a Plugwise device node.""" diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index fb099c9f2..7d9c660d4 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -25,6 +25,8 @@ RelayConfig, RelayLock, RelayState, + Temperature, + Humidity, ) from ..connection import StickController from ..constants import SUPPRESS_INITIALIZATION_WARNINGS, TYPE_MODEL, UTF8 @@ -263,6 +265,26 @@ def motion_state(self) -> MotionState: raise FeatureError(f"Motion state is not supported for node {self.mac}") raise NotImplementedError() + @property + @raise_not_loaded + def temperature(self) -> Temperature: + """Temperature configuration settings.""" + if NodeFeature.TEMPERATURE not in self._features: + raise FeatureError( + f"Temperature configuration is not supported for node {self.mac}" + ) + raise NotImplementedError() + + @property + @raise_not_loaded + def humidity(self) -> Humidity: + """Humidity configuration settings.""" + if NodeFeature.HUMIDITY not in self._features: + raise FeatureError( + f"Humidity configuration is not supported for node {self.mac}" + ) + raise NotImplementedError() + @property def ping_stats(self) -> NetworkStatistics: """Ping statistics.""" diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 111c0efb7..49b8d9985 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -6,7 +6,7 @@ import logging from typing import Any, Final -from ..api import NodeEvent, NodeFeature +from ..api import NodeEvent, NodeFeature, Temperature, Humidity from ..connection import StickController from ..exceptions import MessageError, NodeError from ..messages.responses import SENSE_REPORT_ID, PlugwiseResponse, SenseReportResponse @@ -43,8 +43,8 @@ def __init__( """Initialize Scan Device.""" super().__init__(mac, address, controller, loaded_callback) - self._humidity: float | None = None - self._temperature: float | None = None + self._humidity: Humidity() + self._temperature: Temperature() self._sense_subscription: Callable[[], None] | None = None @@ -56,16 +56,17 @@ async def load(self) -> bool: self._node_info.is_battery_powered = True if self._cache_enabled: _LOGGER.debug("Loading Sense node %s from cache", self._node_info.mac) - if await self._load_from_cache(): - self._loaded = True - self._setup_protocol( - SENSE_FIRMWARE_SUPPORT, - (NodeFeature.INFO, NodeFeature.TEMPERATURE, NodeFeature.HUMIDITY), - ) - if await self.initialize(): - await self._loaded_callback(NodeEvent.LOADED, self.mac) - return True - + await self._load_from_cache() + else: + self._load_defaults() + self._loaded = True + self._setup_protocol( + SENSE_FIRMWARE_SUPPORT, + (NodeFeature.INFO, NodeFeature.TEMPERATURE, NodeFeature.HUMIDITY), + ) + if await self.initialize(): + await self._loaded_callback(NodeEvent.LOADED, self.mac) + return True _LOGGER.debug("Loading of Sense node %s failed", self._node_info.mac) return False @@ -90,6 +91,32 @@ async def unload(self) -> None: self._sense_subscription() await super().unload() + def _load_defaults(self) -> None: + """Load default configuration settings.""" + super()._load_defaults() + self._temperature = Temperature( + temperature = 0.0, + ) + self._humidity = Humidity( + humidity = 0.0, + ) + # region properties + + @property + @raise_not_loaded + def temperature(self) -> Temperature: + """Temperature. """ + return self._temperature + + + @property + @raise_not_loaded + def humidity(self) -> Humidity: + """Humidity. """ + return self._humidity + + # end region + async def _sense_report(self, response: PlugwiseResponse) -> bool: """Process sense report message to extract current temperature and humidity values.""" if not isinstance(response, SenseReportResponse): @@ -99,7 +126,7 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: report_received = False await self._available_update_state(True, response.timestamp) if response.temperature.value != 65535: - self._temperature = int( + self._temperature.temperature = float( SENSE_TEMPERATURE_MULTIPLIER * (response.temperature.value / 65536) - SENSE_TEMPERATURE_OFFSET ) @@ -109,7 +136,7 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: report_received = True if response.humidity.value != 65535: - self._humidity = int( + self._humidity.humidity = float( SENSE_HUMIDITY_MULTIPLIER * (response.humidity.value / 65536) - SENSE_HUMIDITY_OFFSET ) From 7bdc6976d7ef683f77ede3a5bea9c25ec31da1b6 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 9 Jun 2025 19:54:54 +0200 Subject: [PATCH 02/16] fix double propertie entry --- plugwise_usb/nodes/node.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index 7d9c660d4..918a53545 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -196,7 +196,7 @@ def features(self) -> tuple[NodeFeature, ...]: @property @raise_not_loaded - def humidity(self) -> float: + def humidity(self) -> Humidity: """Humidity state.""" if NodeFeature.HUMIDITY not in self._features: raise FeatureError(f"Humidity state is not supported for node {self.mac}") @@ -265,26 +265,6 @@ def motion_state(self) -> MotionState: raise FeatureError(f"Motion state is not supported for node {self.mac}") raise NotImplementedError() - @property - @raise_not_loaded - def temperature(self) -> Temperature: - """Temperature configuration settings.""" - if NodeFeature.TEMPERATURE not in self._features: - raise FeatureError( - f"Temperature configuration is not supported for node {self.mac}" - ) - raise NotImplementedError() - - @property - @raise_not_loaded - def humidity(self) -> Humidity: - """Humidity configuration settings.""" - if NodeFeature.HUMIDITY not in self._features: - raise FeatureError( - f"Humidity configuration is not supported for node {self.mac}" - ) - raise NotImplementedError() - @property def ping_stats(self) -> NetworkStatistics: """Ping statistics.""" @@ -342,7 +322,7 @@ def switch(self) -> bool: @property @raise_not_loaded - def temperature(self) -> float: + def temperature(self) -> Temperature: """Temperature value.""" if NodeFeature.TEMPERATURE not in self._features: raise FeatureError( From 7d2dec2c46fae945fa7ee7c2788b36c54e77ffd2 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 9 Jun 2025 20:09:44 +0200 Subject: [PATCH 03/16] CodeRabbit advice, fix assigning propertie object --- plugwise_usb/nodes/sense.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 49b8d9985..3544a644f 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -43,8 +43,8 @@ def __init__( """Initialize Scan Device.""" super().__init__(mac, address, controller, loaded_callback) - self._humidity: Humidity() - self._temperature: Temperature() + self._humidity = Humidity() + self._temperature = Temperature() self._sense_subscription: Callable[[], None] | None = None From aebfd88f1372394ccfc687f56b3c8eb52cf08348 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Mon, 9 Jun 2025 21:10:08 +0200 Subject: [PATCH 04/16] nitpick fixes --- plugwise_usb/nodes/sense.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 3544a644f..a2d3ec148 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -100,22 +100,22 @@ def _load_defaults(self) -> None: self._humidity = Humidity( humidity = 0.0, ) - # region properties +# region properties @property @raise_not_loaded def temperature(self) -> Temperature: - """Temperature. """ + """Temperature.""" return self._temperature @property @raise_not_loaded def humidity(self) -> Humidity: - """Humidity. """ + """Humidity.""" return self._humidity - # end region +# end region async def _sense_report(self, response: PlugwiseResponse) -> bool: """Process sense report message to extract current temperature and humidity values.""" From 585a20308b0b2fa381209b2f55fe3d0333efaffa Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 08:53:46 +0200 Subject: [PATCH 05/16] consolidate sense data into single propertie --- plugwise_usb/api.py | 9 ++------ plugwise_usb/nodes/node.py | 17 +++++---------- plugwise_usb/nodes/sense.py | 41 +++++++++++++++---------------------- 3 files changed, 23 insertions(+), 44 deletions(-) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 3ca553eee..00edfb552 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -230,15 +230,10 @@ class EnergyStatistics: day_production_reset: datetime | None = None @dataclass -class Temperature: - """Temperature statistics collection.""" +class SenseStatistics: + """Sense statistics collection.""" temperature: float | None = None - -@dataclass -class Humidity: - """Humidity statistics collection.""" - humidity: float | None = None class PlugwiseNode(Protocol): diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index 918a53545..3bdf4681d 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -25,8 +25,7 @@ RelayConfig, RelayLock, RelayState, - Temperature, - Humidity, + SenseStatistics, ) from ..connection import StickController from ..constants import SUPPRESS_INITIALIZATION_WARNINGS, TYPE_MODEL, UTF8 @@ -194,14 +193,6 @@ def features(self) -> tuple[NodeFeature, ...]: """Supported feature types of node.""" return self._features - @property - @raise_not_loaded - def humidity(self) -> Humidity: - """Humidity state.""" - if NodeFeature.HUMIDITY not in self._features: - raise FeatureError(f"Humidity state is not supported for node {self.mac}") - raise NotImplementedError() - @property def is_battery_powered(self) -> bool: """Return if node is battery powered.""" @@ -322,12 +313,14 @@ def switch(self) -> bool: @property @raise_not_loaded - def temperature(self) -> Temperature: - """Temperature value.""" + def sense(self) -> SenseStatistics: + """Sense statistics.""" if NodeFeature.TEMPERATURE not in self._features: raise FeatureError( f"Temperature state is not supported for node {self.mac}" ) + if NodeFeature.HUMIDITY not in self._features: + raise FeatureError(f"Humidity state is not supported for node {self.mac}") raise NotImplementedError() # endregion diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index a2d3ec148..a10ecb876 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -6,7 +6,7 @@ import logging from typing import Any, Final -from ..api import NodeEvent, NodeFeature, Temperature, Humidity +from ..api import NodeEvent, NodeFeature, SenseStatistics from ..connection import StickController from ..exceptions import MessageError, NodeError from ..messages.responses import SENSE_REPORT_ID, PlugwiseResponse, SenseReportResponse @@ -43,8 +43,7 @@ def __init__( """Initialize Scan Device.""" super().__init__(mac, address, controller, loaded_callback) - self._humidity = Humidity() - self._temperature = Temperature() + self._sense_statistics = SenseStatistics() self._sense_subscription: Callable[[], None] | None = None @@ -94,26 +93,18 @@ async def unload(self) -> None: def _load_defaults(self) -> None: """Load default configuration settings.""" super()._load_defaults() - self._temperature = Temperature( - temperature = 0.0, + self._sense_statistics = SenseStatistics( + temperature=0.0, + humidity=0.0, ) - self._humidity = Humidity( - humidity = 0.0, - ) -# region properties - - @property - @raise_not_loaded - def temperature(self) -> Temperature: - """Temperature.""" - return self._temperature - +# region properties + @property @raise_not_loaded - def humidity(self) -> Humidity: - """Humidity.""" - return self._humidity + def sense_statistics(self) -> SenseStatistics: + """Sense Statistics.""" + return self._sense_statistics # end region @@ -126,22 +117,22 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: report_received = False await self._available_update_state(True, response.timestamp) if response.temperature.value != 65535: - self._temperature.temperature = float( + self._sense_statistics.temperature = float( SENSE_TEMPERATURE_MULTIPLIER * (response.temperature.value / 65536) - SENSE_TEMPERATURE_OFFSET ) await self.publish_feature_update_to_subscribers( - NodeFeature.TEMPERATURE, self._temperature + NodeFeature.TEMPERATURE, self._sense_statistics ) report_received = True if response.humidity.value != 65535: - self._humidity.humidity = float( + self._sense_statistics.humidity = float( SENSE_HUMIDITY_MULTIPLIER * (response.humidity.value / 65536) - SENSE_HUMIDITY_OFFSET ) await self.publish_feature_update_to_subscribers( - NodeFeature.HUMIDITY, self._humidity + NodeFeature.HUMIDITY, self._sense_statistics ) report_received = True @@ -164,9 +155,9 @@ async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any match feature: case NodeFeature.TEMPERATURE: - states[NodeFeature.TEMPERATURE] = self._temperature + states[NodeFeature.TEMPERATURE] = self._sense_statistics case NodeFeature.HUMIDITY: - states[NodeFeature.HUMIDITY] = self._humidity + states[NodeFeature.HUMIDITY] = self._sense_statistics case NodeFeature.PING: states[NodeFeature.PING] = await self.ping_update() case _: From 2efaa55253bc48ec580bbfb48d315824237ba32c Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 09:19:15 +0200 Subject: [PATCH 06/16] try 1; fix test --- tests/test_usb.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index 4d6eed36c..a6203ff35 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -614,9 +614,7 @@ async def test_stick_node_discovered_subscription( with pytest.raises(pw_exceptions.FeatureError): assert stick.nodes["5555555555555555"].power with pytest.raises(pw_exceptions.FeatureError): - assert stick.nodes["5555555555555555"].humidity - with pytest.raises(pw_exceptions.FeatureError): - assert stick.nodes["5555555555555555"].temperature + assert stick.nodes["5555555555555555"].sense_statistics with pytest.raises(pw_exceptions.FeatureError): assert stick.nodes["5555555555555555"].energy From 0e2381e5aeb51113a58f58d5962e16f252f9fc22 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 09:23:55 +0200 Subject: [PATCH 07/16] try 2; fix test --- tests/test_usb.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_usb.py b/tests/test_usb.py index a6203ff35..be3241524 100644 --- a/tests/test_usb.py +++ b/tests/test_usb.py @@ -614,7 +614,7 @@ async def test_stick_node_discovered_subscription( with pytest.raises(pw_exceptions.FeatureError): assert stick.nodes["5555555555555555"].power with pytest.raises(pw_exceptions.FeatureError): - assert stick.nodes["5555555555555555"].sense_statistics + assert stick.nodes["5555555555555555"].sense with pytest.raises(pw_exceptions.FeatureError): assert stick.nodes["5555555555555555"].energy @@ -845,9 +845,7 @@ async def test_node_relay_and_power(self, monkeypatch: pytest.MonkeyPatch) -> No with pytest.raises(pw_exceptions.FeatureError): assert stick.nodes["0098765432101234"].switch with pytest.raises(pw_exceptions.FeatureError): - assert stick.nodes["0098765432101234"].humidity - with pytest.raises(pw_exceptions.FeatureError): - assert stick.nodes["0098765432101234"].temperature + assert stick.nodes["0098765432101234"].sense # Test relay init # load node 2222222222222222 which has From 9039c59f9b4f4536e3a6125d50c8036bc5402b9f Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 09:35:00 +0200 Subject: [PATCH 08/16] nitpick comment --- plugwise_usb/nodes/sense.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index a10ecb876..7dee9b93e 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -99,7 +99,7 @@ def _load_defaults(self) -> None: ) # region properties - + @property @raise_not_loaded def sense_statistics(self) -> SenseStatistics: From 8b34bc83028b1d76607774624222cc6ff7e2379e Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Jun 2025 09:55:59 +0200 Subject: [PATCH 09/16] Add NodeFeature SENSE --- plugwise_usb/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugwise_usb/api.py b/plugwise_usb/api.py index 00edfb552..52bdafb96 100644 --- a/plugwise_usb/api.py +++ b/plugwise_usb/api.py @@ -52,6 +52,7 @@ class NodeFeature(str, Enum): RELAY_INIT = "relay_init" RELAY_LOCK = "relay_lock" SWITCH = "switch" + SENSE = "sense" TEMPERATURE = "temperature" @@ -80,6 +81,7 @@ class NodeType(Enum): NodeFeature.MOTION, NodeFeature.MOTION_CONFIG, NodeFeature.TEMPERATURE, + NodeFeature.SENSE, NodeFeature.SWITCH, ) From 38808f91173d4e295ea80c4659f5ea5617a49e73 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Jun 2025 10:10:03 +0200 Subject: [PATCH 10/16] And implement --- plugwise_usb/nodes/node.py | 7 ++----- plugwise_usb/nodes/sense.py | 24 +++++++++--------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/plugwise_usb/nodes/node.py b/plugwise_usb/nodes/node.py index 3bdf4681d..13b6433ea 100644 --- a/plugwise_usb/nodes/node.py +++ b/plugwise_usb/nodes/node.py @@ -315,13 +315,10 @@ def switch(self) -> bool: @raise_not_loaded def sense(self) -> SenseStatistics: """Sense statistics.""" - if NodeFeature.TEMPERATURE not in self._features: + if NodeFeature.SENSE not in self._features: raise FeatureError( - f"Temperature state is not supported for node {self.mac}" + f"Sense statistics is not supported for node {self.mac}" ) - if NodeFeature.HUMIDITY not in self._features: - raise FeatureError(f"Humidity state is not supported for node {self.mac}") - raise NotImplementedError() # endregion diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 7dee9b93e..41ec1d497 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -25,8 +25,7 @@ SENSE_FEATURES: Final = ( NodeFeature.INFO, - NodeFeature.TEMPERATURE, - NodeFeature.HUMIDITY, + NodeFeature.SENSE, ) @@ -61,7 +60,7 @@ async def load(self) -> bool: self._loaded = True self._setup_protocol( SENSE_FIRMWARE_SUPPORT, - (NodeFeature.INFO, NodeFeature.TEMPERATURE, NodeFeature.HUMIDITY), + (NodeFeature.INFO, NodeFeature.SENSE), ) if await self.initialize(): await self._loaded_callback(NodeEvent.LOADED, self.mac) @@ -114,17 +113,14 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: raise MessageError( f"Invalid response message type ({response.__class__.__name__}) received, expected SenseReportResponse" ) - report_received = False + report_received_1 = report_received_2 = False await self._available_update_state(True, response.timestamp) if response.temperature.value != 65535: self._sense_statistics.temperature = float( SENSE_TEMPERATURE_MULTIPLIER * (response.temperature.value / 65536) - SENSE_TEMPERATURE_OFFSET ) - await self.publish_feature_update_to_subscribers( - NodeFeature.TEMPERATURE, self._sense_statistics - ) - report_received = True + report_received_1 = True if response.humidity.value != 65535: self._sense_statistics.humidity = float( @@ -132,11 +128,11 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: - SENSE_HUMIDITY_OFFSET ) await self.publish_feature_update_to_subscribers( - NodeFeature.HUMIDITY, self._sense_statistics + NodeFeature.SENSE, self._sense_statistics ) - report_received = True + report_received_2 = True - return report_received + return report_received_1 and report_received_2 @raise_not_loaded async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any]: @@ -154,12 +150,10 @@ async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any ) match feature: - case NodeFeature.TEMPERATURE: - states[NodeFeature.TEMPERATURE] = self._sense_statistics - case NodeFeature.HUMIDITY: - states[NodeFeature.HUMIDITY] = self._sense_statistics case NodeFeature.PING: states[NodeFeature.PING] = await self.ping_update() + case NodeFeature.SENSE: + states[NodeFeature.TEMPERATURE] = self._sense_statistics case _: state_result = await super().get_state((feature,)) states[feature] = state_result[feature] From df3fe4aa6bd9e8b103d1037e4c4f019723a29989 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 10:40:30 +0200 Subject: [PATCH 11/16] Apply refactor suggestions --- plugwise_usb/nodes/sense.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 41ec1d497..3ad0d2c8d 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -57,7 +57,6 @@ async def load(self) -> bool: await self._load_from_cache() else: self._load_defaults() - self._loaded = True self._setup_protocol( SENSE_FIRMWARE_SUPPORT, (NodeFeature.INFO, NodeFeature.SENSE), @@ -153,7 +152,7 @@ async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any case NodeFeature.PING: states[NodeFeature.PING] = await self.ping_update() case NodeFeature.SENSE: - states[NodeFeature.TEMPERATURE] = self._sense_statistics + states[NodeFeature.SENSE] = self._sense_statistics case _: state_result = await super().get_state((feature,)) states[feature] = state_result[feature] From 61021e828d5a754d148bc64f4e6b9c5adf569931 Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 10:57:57 +0200 Subject: [PATCH 12/16] re-add self._loaded --- plugwise_usb/nodes/sense.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 3ad0d2c8d..a51939cbd 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -63,6 +63,7 @@ async def load(self) -> bool: ) if await self.initialize(): await self._loaded_callback(NodeEvent.LOADED, self.mac) + self._loaded = True return True _LOGGER.debug("Loading of Sense node %s failed", self._node_info.mac) return False From 00288e17f77d5be2ae9921595ec6b6ced8e9f29f Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Jun 2025 11:08:33 +0200 Subject: [PATCH 13/16] Fix publish_feature --- plugwise_usb/nodes/sense.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index a51939cbd..6dd808198 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -127,11 +127,12 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: SENSE_HUMIDITY_MULTIPLIER * (response.humidity.value / 65536) - SENSE_HUMIDITY_OFFSET ) - await self.publish_feature_update_to_subscribers( - NodeFeature.SENSE, self._sense_statistics - ) report_received_2 = True - + + await self.publish_feature_update_to_subscribers( + NodeFeature.SENSE, self._sense_statistics + ) + return report_received_1 and report_received_2 @raise_not_loaded From 6be4c4c0cf9e15f6a202960420c57a306a2d712c Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 11:32:10 +0200 Subject: [PATCH 14/16] move self._loaded because initialize requires this to be set --- plugwise_usb/nodes/sense.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 6dd808198..59ee95060 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -61,9 +61,9 @@ async def load(self) -> bool: SENSE_FIRMWARE_SUPPORT, (NodeFeature.INFO, NodeFeature.SENSE), ) + self._loaded = True if await self.initialize(): await self._loaded_callback(NodeEvent.LOADED, self.mac) - self._loaded = True return True _LOGGER.debug("Loading of Sense node %s failed", self._node_info.mac) return False From 429cf3675ad211b054921611984fa71f84c6e5c1 Mon Sep 17 00:00:00 2001 From: Bouwe Westerdijk Date: Tue, 10 Jun 2025 11:37:27 +0200 Subject: [PATCH 15/16] report_received improvements --- plugwise_usb/nodes/sense.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 59ee95060..876ac56d7 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -113,27 +113,28 @@ async def _sense_report(self, response: PlugwiseResponse) -> bool: raise MessageError( f"Invalid response message type ({response.__class__.__name__}) received, expected SenseReportResponse" ) - report_received_1 = report_received_2 = False + report_received = False await self._available_update_state(True, response.timestamp) if response.temperature.value != 65535: self._sense_statistics.temperature = float( SENSE_TEMPERATURE_MULTIPLIER * (response.temperature.value / 65536) - SENSE_TEMPERATURE_OFFSET ) - report_received_1 = True + report_received = True if response.humidity.value != 65535: self._sense_statistics.humidity = float( SENSE_HUMIDITY_MULTIPLIER * (response.humidity.value / 65536) - SENSE_HUMIDITY_OFFSET ) - report_received_2 = True + report_received = True - await self.publish_feature_update_to_subscribers( - NodeFeature.SENSE, self._sense_statistics - ) + if report_received: + await self.publish_feature_update_to_subscribers( + NodeFeature.SENSE, self._sense_statistics + ) - return report_received_1 and report_received_2 + return report_received @raise_not_loaded async def get_state(self, features: tuple[NodeFeature]) -> dict[NodeFeature, Any]: From 9676a39be6fb865bb0bc016cc15a6d473137dcaf Mon Sep 17 00:00:00 2001 From: Marc Dirix Date: Tue, 10 Jun 2025 12:09:33 +0200 Subject: [PATCH 16/16] fix publication of NodeFeature.SENSE combine report_received and check for true before propagating to integration --- plugwise_usb/nodes/helpers/firmware.py | 1 + plugwise_usb/nodes/sense.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugwise_usb/nodes/helpers/firmware.py b/plugwise_usb/nodes/helpers/firmware.py index b909e7b42..a0298295d 100644 --- a/plugwise_usb/nodes/helpers/firmware.py +++ b/plugwise_usb/nodes/helpers/firmware.py @@ -156,6 +156,7 @@ class SupportedVersions(NamedTuple): FEATURE_SUPPORTED_AT_FIRMWARE: Final = { NodeFeature.BATTERY: 2.0, NodeFeature.INFO: 2.0, + NodeFeature.SENSE: 2.0, NodeFeature.TEMPERATURE: 2.0, NodeFeature.HUMIDITY: 2.0, NodeFeature.ENERGY: 2.0, diff --git a/plugwise_usb/nodes/sense.py b/plugwise_usb/nodes/sense.py index 876ac56d7..e1beaf5a8 100644 --- a/plugwise_usb/nodes/sense.py +++ b/plugwise_usb/nodes/sense.py @@ -57,11 +57,11 @@ async def load(self) -> bool: await self._load_from_cache() else: self._load_defaults() + self._loaded = True self._setup_protocol( SENSE_FIRMWARE_SUPPORT, (NodeFeature.INFO, NodeFeature.SENSE), ) - self._loaded = True if await self.initialize(): await self._loaded_callback(NodeEvent.LOADED, self.mac) return True