Skip to content

Commit 5b5350d

Browse files
authored
Merge pull request #280 from plugwise/mdi_switch
prevent multiple subscriptions to the same NodeFeature
2 parents 82896fc + 0f48ce9 commit 5b5350d

6 files changed

Lines changed: 49 additions & 36 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Versions from 0.4x
44

5+
### v0.55.4 - 2025-07-08
6+
7+
- PR [280](https://github.com/plugwise/plugwise_usb-beta/pull/280): Prevent multiple NodeFeature subscriptions
8+
59
### v0.55.3 - 2025-07-08
610

711
- Bump plugwise to [v0.44.7](https://github.com/plugwise/python-plugwise-usb/releases/tag/v0.44.7)

custom_components/plugwise_usb/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ async def async_unload_entry(
163163
) -> bool:
164164
"""Unload the Plugwise USB stick connection."""
165165
config_entry.runtime_data[UNSUBSCRIBE_DISCOVERY]()
166+
for coordinator in config_entry.runtime_data[NODES].values():
167+
coordinator.unsubscribe_all_nodefeatures()
166168
unload = await hass.config_entries.async_unload_platforms(
167169
config_entry, PLUGWISE_USB_PLATFORMS
168170
)

custom_components/plugwise_usb/coordinator.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from collections import Counter
44
from datetime import timedelta
55
import logging
6-
from typing import Any
6+
from typing import Any, Callable
77

8-
from plugwise_usb.api import NodeFeature, PlugwiseNode
8+
from plugwise_usb.api import PUSHING_FEATURES, NodeFeature, PlugwiseNode
99
from plugwise_usb.exceptions import NodeError, NodeTimeout, StickError, StickTimeout
1010

1111
from homeassistant.config_entries import ConfigEntry
@@ -37,6 +37,9 @@ def __init__(
3737
) -> None:
3838
"""Initialize Plugwise USB data update coordinator."""
3939
self.node = node
40+
self.subscribed_nodefeatures: list[NodeFeature] = []
41+
self._subscribe_to_feature_fn = self.node.subscribe_to_feature_update
42+
self.unsubscribe_push_events: list[Callable[[], None]] = []
4043
if node.node_info.is_battery_powered:
4144
_LOGGER.debug("Create battery powered DUC for %s", node.mac)
4245
super().__init__(
@@ -86,3 +89,35 @@ async def async_node_update(self) -> dict[NodeFeature, Any]:
8689
raise UpdateFailed("Device is not responding")
8790

8891
return states
92+
93+
async def subscribe_nodefeature(self, node_feature: NodeFeature) -> None:
94+
"""Subscribe to a nodefeature."""
95+
if (
96+
node_feature in PUSHING_FEATURES
97+
and node_feature in self.node.node_info.features
98+
and node_feature not in self.subscribed_nodefeatures
99+
):
100+
self.unsubscribe_push_events.append(
101+
self._subscribe_to_feature_fn(
102+
self.async_push_event,
103+
node_feature,
104+
)
105+
)
106+
self.subscribed_nodefeatures.append(node_feature)
107+
108+
async def async_push_event(self, feature: NodeFeature, state: Any) -> None:
109+
"""Update data pushed by node."""
110+
self.async_set_updated_data(
111+
{
112+
feature: state,
113+
}
114+
)
115+
116+
async def unsubscribe_all_nodefeatures(self) -> None:
117+
"""Unsubscribe to updates."""
118+
for unsubscribe_push_event in self.unsubscribe_push_events:
119+
if unsubscribe_push_event is not None:
120+
unsubscribe_push_event()
121+
self.unsubscribe_push_events.clear()
122+
self.subscribed_nodefeatures.clear()
123+

custom_components/plugwise_usb/entity.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Callable
65
from dataclasses import dataclass
76
import logging
8-
from typing import Any
97

10-
from plugwise_usb.api import PUSHING_FEATURES, NodeFeature, NodeInfo
8+
from plugwise_usb.api import NodeFeature, NodeInfo
119

1210
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo
1311
from homeassistant.helpers.entity import EntityDescription
@@ -40,10 +38,8 @@ def __init__(
4038
super().__init__(node_duc, context=entity_description.node_feature)
4139
self.node_duc = node_duc
4240
self.entity_description = entity_description
43-
self.unsubscribe_push_events: Callable[[], None] | None = None
4441
self._node_info: NodeInfo = node_duc.node.node_info
4542
self._attr_unique_id = f"{self._node_info.mac}-{entity_description.key}"
46-
self._subscribe_to_feature_fn = node_duc.node.subscribe_to_feature_update
4743
self._via_device = (DOMAIN, str(node_duc.api_stick.mac_stick))
4844

4945
@property
@@ -70,34 +66,10 @@ def device_info(self) -> DeviceInfo:
7066
async def async_added_to_hass(self):
7167
"""Subscribe for push updates."""
7268
await super().async_added_to_hass()
73-
if (
74-
self.entity_description.node_feature in PUSHING_FEATURES
75-
and self.entity_description.node_feature in self._node_info.features
76-
):
77-
self.unsubscribe_push_events = self._subscribe_to_feature_fn(
78-
self.async_push_event,
79-
self.entity_description.node_feature,
80-
)
81-
82-
async def async_push_event(self, feature: NodeFeature, state: Any) -> None:
83-
"""Update data on pushed event."""
84-
if self.node_duc is None:
85-
_LOGGER.warning(
86-
"Unable to push event=%s, state=%s, mac=%s",
87-
feature,
88-
state,
89-
self._node_info.mac,
90-
)
91-
else:
92-
self.node_duc.async_set_updated_data(
93-
{
94-
feature: state,
95-
}
96-
)
69+
await self.node_duc.subscribe_nodefeature(
70+
self.entity_description.node_feature
71+
)
9772

9873
async def async_will_remove_from_hass(self):
9974
"""Unsubscribe to updates."""
100-
if self.unsubscribe_push_events is not None:
101-
self.unsubscribe_push_events()
102-
self.unsubscribe_push_events = None
10375
await super().async_will_remove_from_hass()

custom_components/plugwise_usb/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
"iot_class": "local_polling",
1010
"loggers": ["plugwise_usb"],
1111
"requirements": ["plugwise-usb==0.44.7"],
12-
"version": "0.55.3"
12+
"version": "0.55.4"
1313
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "plugwise_usb-beta"
3-
version = "0.55.3"
3+
version = "0.55.4"
44
description = "Plugwise USB custom_component (BETA)"
55
readme = "README.md"
66
requires-python = ">=3.13"

0 commit comments

Comments
 (0)