Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions plugwise_usb/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class NodeFeature(str, Enum):
RELAY_INIT = "relay_init"
RELAY_LOCK = "relay_lock"
SWITCH = "switch"
SENSE = "sense"
TEMPERATURE = "temperature"


Expand Down Expand Up @@ -80,6 +81,7 @@ class NodeType(Enum):
NodeFeature.MOTION,
NodeFeature.MOTION_CONFIG,
NodeFeature.TEMPERATURE,
NodeFeature.SENSE,
NodeFeature.SWITCH,
)

Expand Down Expand Up @@ -229,6 +231,12 @@ class EnergyStatistics:
day_production: float | None = None
day_production_reset: datetime | None = None

@dataclass
class SenseStatistics:
"""Sense statistics collection."""

temperature: float | None = None
humidity: float | None = None

class PlugwiseNode(Protocol):
"""Protocol definition of a Plugwise device node."""
Expand Down
1 change: 1 addition & 0 deletions plugwise_usb/nodes/helpers/firmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
18 changes: 5 additions & 13 deletions plugwise_usb/nodes/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
RelayConfig,
RelayLock,
RelayState,
SenseStatistics,
)
from ..connection import StickController
from ..constants import SUPPRESS_INITIALIZATION_WARNINGS, TYPE_MODEL, UTF8
Expand Down Expand Up @@ -192,14 +193,6 @@ def features(self) -> tuple[NodeFeature, ...]:
"""Supported feature types of node."""
return self._features

@property
@raise_not_loaded
def humidity(self) -> float:
"""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."""
Expand Down Expand Up @@ -320,13 +313,12 @@ def switch(self) -> bool:

@property
@raise_not_loaded
def temperature(self) -> float:
"""Temperature value."""
if NodeFeature.TEMPERATURE not in self._features:
def sense(self) -> SenseStatistics:
"""Sense statistics."""
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}"
)
raise NotImplementedError()

# endregion

Expand Down
68 changes: 41 additions & 27 deletions plugwise_usb/nodes/sense.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import logging
from typing import Any, Final

from ..api import NodeEvent, NodeFeature
from ..api import NodeEvent, NodeFeature, SenseStatistics
from ..connection import StickController
from ..exceptions import MessageError, NodeError
from ..messages.responses import SENSE_REPORT_ID, PlugwiseResponse, SenseReportResponse
Expand All @@ -25,8 +25,7 @@

SENSE_FEATURES: Final = (
NodeFeature.INFO,
NodeFeature.TEMPERATURE,
NodeFeature.HUMIDITY,
NodeFeature.SENSE,
)


Expand All @@ -43,8 +42,7 @@
"""Initialize Scan Device."""
super().__init__(mac, address, controller, loaded_callback)

self._humidity: float | None = None
self._temperature: float | None = None
self._sense_statistics = SenseStatistics()

Check warning on line 45 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L45

Added line #L45 was not covered by tests

self._sense_subscription: Callable[[], None] | None = None

Expand All @@ -56,16 +54,17 @@
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()

Check warning on line 57 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L57

Added line #L57 was not covered by tests
else:
self._load_defaults()
self._loaded = True
self._setup_protocol(

Check warning on line 61 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L59-L61

Added lines #L59 - L61 were not covered by tests
SENSE_FIRMWARE_SUPPORT,
(NodeFeature.INFO, NodeFeature.SENSE),
)
if await self.initialize():
await self._loaded_callback(NodeEvent.LOADED, self.mac)
return True

Check warning on line 67 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L65-L67

Added lines #L65 - L67 were not covered by tests
_LOGGER.debug("Loading of Sense node %s failed", self._node_info.mac)
return False

Expand All @@ -90,6 +89,24 @@
self._sense_subscription()
await super().unload()

def _load_defaults(self) -> None:
"""Load default configuration settings."""
super()._load_defaults()
self._sense_statistics = SenseStatistics(

Check warning on line 95 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L94-L95

Added lines #L94 - L95 were not covered by tests
temperature=0.0,
humidity=0.0,
)

# region properties

@property
@raise_not_loaded
def sense_statistics(self) -> SenseStatistics:
"""Sense Statistics."""
return self._sense_statistics

Check warning on line 106 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L106

Added line #L106 was not covered by tests

# 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):
Expand All @@ -99,25 +116,24 @@
report_received = False
await self._available_update_state(True, response.timestamp)
if response.temperature.value != 65535:
self._temperature = int(
self._sense_statistics.temperature = float(

Check warning on line 119 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L119

Added line #L119 was not covered by tests
SENSE_TEMPERATURE_MULTIPLIER * (response.temperature.value / 65536)
- SENSE_TEMPERATURE_OFFSET
)
await self.publish_feature_update_to_subscribers(
NodeFeature.TEMPERATURE, self._temperature
)
report_received = True

if response.humidity.value != 65535:
self._humidity = int(
self._sense_statistics.humidity = float(

Check warning on line 126 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L126

Added line #L126 was not covered by tests
SENSE_HUMIDITY_MULTIPLIER * (response.humidity.value / 65536)
- SENSE_HUMIDITY_OFFSET
)
report_received = True

Check warning on line 130 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L130

Added line #L130 was not covered by tests

if report_received:

Check warning on line 132 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L132

Added line #L132 was not covered by tests
await self.publish_feature_update_to_subscribers(
NodeFeature.HUMIDITY, self._humidity
NodeFeature.SENSE, self._sense_statistics
)
report_received = True


return report_received

@raise_not_loaded
Expand All @@ -136,12 +152,10 @@
)

match feature:
case NodeFeature.TEMPERATURE:
states[NodeFeature.TEMPERATURE] = self._temperature
case NodeFeature.HUMIDITY:
states[NodeFeature.HUMIDITY] = self._humidity
case NodeFeature.PING:
states[NodeFeature.PING] = await self.ping_update()
case NodeFeature.SENSE:
states[NodeFeature.SENSE] = self._sense_statistics

Check warning on line 158 in plugwise_usb/nodes/sense.py

View check run for this annotation

Codecov / codecov/patch

plugwise_usb/nodes/sense.py#L157-L158

Added lines #L157 - L158 were not covered by tests
case _:
state_result = await super().get_state((feature,))
states[feature] = state_result[feature]
Expand Down
8 changes: 2 additions & 6 deletions tests/test_usb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
with pytest.raises(pw_exceptions.FeatureError):
assert stick.nodes["5555555555555555"].energy

Expand Down Expand Up @@ -847,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
Expand Down
Loading