Skip to content

Commit 1fc11c5

Browse files
authored
Merge pull request #220 from plugwise/add_stick_2
Add Stick as device, use it as via_device for the connected devices
2 parents cfa1bf5 + 4586443 commit 1fc11c5

File tree

12 files changed

+114
-41
lines changed

12 files changed

+114
-41
lines changed

custom_components/plugwise_usb/__init__.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
import logging
44
from typing import Any, TypedDict
55

6-
from homeassistant.config_entries import ConfigEntry
76
from homeassistant.core import HomeAssistant, callback
87
from homeassistant.exceptions import ConfigEntryNotReady
9-
from homeassistant.helpers import entity_registry as er
8+
from homeassistant.helpers import device_registry as dr, entity_registry as er
109
from homeassistant.helpers.storage import STORAGE_DIR
1110
from plugwise_usb import Stick
1211
from plugwise_usb.api import NodeEvent
1312
from plugwise_usb.exceptions import StickError
1413

1514
from .const import (
1615
CONF_USB_PATH,
16+
DOMAIN,
1717
NODES,
1818
PLUGWISE_USB_PLATFORMS,
1919
STICK,
2020
)
21-
from .coordinator import PlugwiseUSBDataUpdateCoordinator
21+
from .coordinator import PlugwiseUSBConfigEntry, PlugwiseUSBDataUpdateCoordinator
2222

2323
_LOGGER = logging.getLogger(__name__)
2424
UNSUBSCRIBE_DISCOVERY = "unsubscribe_discovery"
@@ -31,7 +31,7 @@ class NodeConfigEntry(TypedDict):
3131
coordinator: PlugwiseUSBDataUpdateCoordinator
3232

3333

34-
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
34+
async def async_setup_entry(hass: HomeAssistant, config_entry: PlugwiseUSBConfigEntry):
3535
"""Establish connection with plugwise USB-stick."""
3636

3737
@callback
@@ -60,6 +60,19 @@ def _async_migrate_entity_entry(
6060
f"Failed to open connection to Plugwise USB stick at {config_entry.data[CONF_USB_PATH]}"
6161
) from StickError
6262

63+
device_registry = dr.async_get(hass)
64+
device_registry.async_get_or_create(
65+
config_entry_id=config_entry.entry_id,
66+
connections={(dr.CONNECTION_ZIGBEE, str(api_stick.mac_stick))},
67+
hw_version=str(api_stick.hardware),
68+
identifiers={(DOMAIN, str(api_stick.mac_stick))},
69+
manufacturer="Plugwise",
70+
model="Stick",
71+
name=str(api_stick.name),
72+
serial_number=str(api_stick.mac_stick),
73+
sw_version=str(api_stick.firmware),
74+
)
75+
6376
config_entry.runtime_data[NODES]: NodeConfigEntry = {}
6477

6578
async def async_node_discovered(node_event: NodeEvent, mac: str) -> None:
@@ -69,7 +82,7 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None:
6982
_LOGGER.debug("async_node_discovered | mac=%s", mac)
7083
node = api_stick.nodes[mac]
7184
_LOGGER.debug("async_node_discovered | node_info=%s", node.node_info)
72-
coordinator = PlugwiseUSBDataUpdateCoordinator(hass, node)
85+
coordinator = PlugwiseUSBDataUpdateCoordinator(hass, config_entry, node)
7386
config_entry.runtime_data[NODES][mac] = coordinator
7487
await node.load()
7588

@@ -94,10 +107,11 @@ async def async_node_discovered(node_event: NodeEvent, mac: str) -> None:
94107

95108
# Initiate background discovery task
96109
hass.async_create_task(api_stick.discover_nodes(load=True))
110+
97111
return True
98112

99113

100-
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
114+
async def async_unload_entry(hass: HomeAssistant, config_entry: PlugwiseUSBConfigEntry):
101115
"""Unload the Plugwise USB stick connection."""
102116
config_entry.runtime_data[UNSUBSCRIBE_DISCOVERY]()
103117
unload = await hass.config_entries.async_unload_platforms(
@@ -109,7 +123,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry):
109123

110124
@callback
111125
def async_migrate_entity_entry(
112-
config_entry: ConfigEntry, entity_entry: er.RegistryEntry
126+
config_entry: PlugwiseUSBConfigEntry, entity_entry: er.RegistryEntry
113127
) -> dict[str, Any] | None:
114128
"""Migrate Plugwise USB entity entries.
115129

custom_components/plugwise_usb/binary_sensor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
BinarySensorEntity,
1212
BinarySensorEntityDescription,
1313
)
14-
from homeassistant.config_entries import ConfigEntry
1514
from homeassistant.const import Platform
1615
from homeassistant.core import HomeAssistant, callback
1716
from homeassistant.helpers.entity_platform import AddEntitiesCallback
1817
from plugwise_usb.api import NodeEvent, NodeFeature
1918

2019
from .const import NODES, STICK, UNSUB_NODE_LOADED
20+
from .coordinator import PlugwiseUSBConfigEntry
2121
from .entity import PlugwiseUSBEntity, PlugwiseUSBEntityDescription
2222

2323
_LOGGER = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ class PlugwiseBinarySensorEntityDescription(
4747

4848
async def async_setup_entry(
4949
_hass: HomeAssistant,
50-
config_entry: ConfigEntry,
50+
config_entry: PlugwiseUSBConfigEntry,
5151
async_add_entities: AddEntitiesCallback,
5252
) -> None:
5353
"""Set up Plugwise USB binary sensor based on config_entry."""
@@ -90,7 +90,7 @@ async def async_add_binary_sensor(node_event: NodeEvent, mac: str) -> None:
9090

9191
async def async_unload_entry(
9292
_hass: HomeAssistant,
93-
config_entry: ConfigEntry,
93+
config_entry: PlugwiseUSBConfigEntry,
9494
) -> None:
9595
"""Unload a config entry."""
9696
config_entry.runtime_data[Platform.SENSOR][UNSUB_NODE_LOADED]()

custom_components/plugwise_usb/coordinator.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import logging
66
from typing import Any
77

8+
from homeassistant.config_entries import ConfigEntry
89
from homeassistant.core import HomeAssistant
910
from homeassistant.helpers.update_coordinator import (
1011
ConfigEntryError,
@@ -14,15 +15,22 @@
1415
from plugwise_usb.api import NodeFeature, PlugwiseNode
1516
from plugwise_usb.exceptions import NodeError, NodeTimeout, StickError, StickTimeout
1617

18+
from .const import STICK
19+
1720
_LOGGER = logging.getLogger(__name__)
1821

22+
type PlugwiseUSBConfigEntry = ConfigEntry[PlugwiseUSBDataUpdateCoordinator]
23+
1924

2025
class PlugwiseUSBDataUpdateCoordinator(DataUpdateCoordinator):
2126
"""Class to manage fetching data from a Plugwise node."""
2227

28+
config_entry: PlugwiseUSBConfigEntry
29+
2330
def __init__(
2431
self,
2532
hass: HomeAssistant,
33+
config_entry: PlugwiseUSBConfigEntry,
2634
node: PlugwiseNode,
2735
update_interval: timedelta | None = None,
2836
) -> None:
@@ -33,6 +41,7 @@ def __init__(
3341
super().__init__(
3442
hass,
3543
_LOGGER,
44+
config_entry=config_entry,
3645
name=node.node_info.name,
3746
update_method=self.async_node_update,
3847
always_update=True,
@@ -50,6 +59,8 @@ def __init__(
5059
always_update=True,
5160
)
5261

62+
self.api_stick = config_entry.runtime_data[STICK]
63+
5364
async def async_node_update(self) -> dict[NodeFeature, Any]:
5465
"""Request status update for Plugwise Node."""
5566
states: dict[NodeFeature, Any] = {}
@@ -72,4 +83,5 @@ async def async_node_update(self) -> dict[NodeFeature, Any]:
7283
and not states[NodeFeature.AVAILABLE].state
7384
):
7485
raise UpdateFailed("Device is not responding")
86+
7587
return states

custom_components/plugwise_usb/entity.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
from typing import Any
99

10+
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE, DeviceInfo
1011
from homeassistant.helpers.entity import EntityDescription
1112
from homeassistant.helpers.update_coordinator import CoordinatorEntity
1213
from plugwise_usb.api import PUSHING_FEATURES, NodeFeature, NodeInfo
@@ -35,22 +36,35 @@ def __init__(
3536
entity_description: PlugwiseUSBEntityDescription,
3637
) -> None:
3738
"""Initialize a Plugwise USB entity."""
38-
self.node_duc = node_duc
3939
super().__init__(node_duc, context=entity_description.node_feature)
40+
self.node_duc = node_duc
41+
self.entity_description = entity_description
42+
self.unsubscribe_push_events: Callable[[], None] | None = None
4043
self._node_info: NodeInfo = node_duc.node.node_info
41-
self._attr_device_info = {
42-
"identifiers": {(DOMAIN, self._node_info.mac)},
43-
"hw_version": self._node_info.version,
44-
"name": f"{self._node_info.name}",
45-
"manufacturer": "Plugwise",
46-
"model": self._node_info.model,
47-
"model_id": self._node_info.model_type,
48-
"sw_version": f"{self._node_info.firmware}",
49-
}
5044
self._attr_unique_id = f"{self._node_info.mac}-{entity_description.key}"
51-
self.entity_description = entity_description
5245
self._subscribe_to_feature_fn = node_duc.node.subscribe_to_feature_update
53-
self.unsubscribe_push_events: Callable[[], None] | None = None
46+
self._via_device = (DOMAIN, str(node_duc.api_stick.mac_stick))
47+
48+
@property
49+
def available(self) -> bool:
50+
"""Return if entity is available."""
51+
return self.node_duc.node.available and super().available
52+
53+
@property
54+
def device_info(self) -> DeviceInfo:
55+
"""Return DeviceInfo for each created entity."""
56+
return DeviceInfo(
57+
connections={(CONNECTION_ZIGBEE, str(self._node_info.mac))},
58+
hw_version=str(self._node_info.version),
59+
identifiers={(DOMAIN, str(self._node_info.mac))},
60+
manufacturer="Plugwise",
61+
model=str(self._node_info.model),
62+
model_id=self._node_info.model_type,
63+
name=str(self._node_info.name),
64+
serial_number=str(self._node_info.mac),
65+
sw_version=str(self._node_info.firmware),
66+
via_device=self._via_device,
67+
)
5468

5569
async def async_added_to_hass(self):
5670
"""Subscribe for push updates."""

custom_components/plugwise_usb/manifest.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
"integration_type": "hub",
99
"iot_class": "local_polling",
1010
"loggers": ["plugwise_usb"],
11-
"requirements": [
12-
"https://test-files.pythonhosted.org/packages/d0/05/f52b59d222cd857284b00aa77e237ffdd25acc8955b5ebc2ff411822448b/plugwise_usb-0.40.0a27.tar.gz#plugwise-usb==0.40.0a27"
13-
],
14-
"version": "0.50.0a11"
11+
"requirements": ["plugwise-usb==0.40.0b1"],
12+
"version": "0.50.0a21"
1513
}

custom_components/plugwise_usb/number.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
NumberEntity,
1212
NumberEntityDescription,
1313
)
14-
from homeassistant.config_entries import ConfigEntry
1514
from homeassistant.const import EntityCategory, Platform, UnitOfTime
1615
from homeassistant.core import HomeAssistant, callback
1716
from homeassistant.helpers.entity_platform import AddEntitiesCallback
1817
from plugwise_usb.api import NodeEvent, NodeFeature
1918

2019
from .const import NODES, STICK, UNSUB_NODE_LOADED
21-
from .coordinator import PlugwiseUSBDataUpdateCoordinator
20+
from .coordinator import PlugwiseUSBConfigEntry, PlugwiseUSBDataUpdateCoordinator
2221
from .entity import PlugwiseUSBEntity, PlugwiseUSBEntityDescription
2322

2423
_LOGGER = logging.getLogger(__name__)
@@ -102,7 +101,7 @@ class PlugwiseNumberEntityDescription(
102101

103102
async def async_setup_entry(
104103
hass: HomeAssistant,
105-
config_entry: ConfigEntry,
104+
config_entry: PlugwiseUSBConfigEntry,
106105
async_add_entities: AddEntitiesCallback,
107106
) -> None:
108107
"""Set up the USB Number from a config entry."""
@@ -143,7 +142,7 @@ async def async_add_number(node_event: NodeEvent, mac: str) -> None:
143142

144143
async def async_unload_entry(
145144
hass: HomeAssistant,
146-
config_entry: ConfigEntry,
145+
config_entry: PlugwiseUSBConfigEntry,
147146
) -> None:
148147
"""Unload a config entry."""
149148
config_entry.runtime_data[Platform.NUMBER][UNSUB_NODE_LOADED]()

custom_components/plugwise_usb/sensor.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
SensorEntityDescription,
1313
SensorStateClass,
1414
)
15-
from homeassistant.config_entries import ConfigEntry
1615
from homeassistant.const import (
1716
PERCENTAGE,
1817
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
@@ -28,6 +27,7 @@
2827
from plugwise_usb.api import NodeEvent, NodeFeature
2928

3029
from .const import NODES, STICK, UNSUB_NODE_LOADED
30+
from .coordinator import PlugwiseUSBConfigEntry
3131
from .entity import PlugwiseUSBEntity, PlugwiseUSBEntityDescription
3232

3333
_LOGGER = logging.getLogger(__name__)
@@ -72,6 +72,16 @@ class PlugwiseSensorEntityDescription(
7272
node_feature=NodeFeature.ENERGY,
7373
entity_registry_enabled_default=False,
7474
),
75+
PlugwiseSensorEntityDescription(
76+
key="hour_production",
77+
translation_key="energy_hour_production",
78+
device_class=SensorDeviceClass.ENERGY,
79+
state_class=SensorStateClass.TOTAL,
80+
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
81+
suggested_display_precision=3,
82+
node_feature=NodeFeature.ENERGY,
83+
entity_registry_enabled_default=False,
84+
),
7585
PlugwiseSensorEntityDescription(
7686
key="day_consumption",
7787
translation_key="energy_day_consumption",
@@ -81,6 +91,16 @@ class PlugwiseSensorEntityDescription(
8191
suggested_display_precision=3,
8292
node_feature=NodeFeature.ENERGY,
8393
),
94+
PlugwiseSensorEntityDescription(
95+
key="day_production",
96+
translation_key="energy_day_production",
97+
device_class=SensorDeviceClass.ENERGY,
98+
state_class=SensorStateClass.TOTAL,
99+
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
100+
suggested_display_precision=3,
101+
node_feature=NodeFeature.ENERGY,
102+
entity_registry_enabled_default=False,
103+
),
84104
PlugwiseSensorEntityDescription(
85105
key="rtt",
86106
translation_key="ping_rrt",
@@ -135,7 +155,6 @@ class PlugwiseSensorEntityDescription(
135155
key="last_seen",
136156
translation_key="last_seen",
137157
device_class=SensorDeviceClass.TIMESTAMP,
138-
state_class=SensorStateClass.MEASUREMENT,
139158
node_feature=NodeFeature.AVAILABLE,
140159
entity_registry_enabled_default=False,
141160
entity_category=EntityCategory.DIAGNOSTIC,
@@ -145,7 +164,7 @@ class PlugwiseSensorEntityDescription(
145164

146165
async def async_setup_entry(
147166
_hass: HomeAssistant,
148-
config_entry: ConfigEntry,
167+
config_entry: PlugwiseUSBConfigEntry,
149168
async_add_entities: AddEntitiesCallback,
150169
) -> None:
151170
"""Set up Plugwise USB sensor based on config_entry."""
@@ -190,7 +209,7 @@ async def async_add_sensor(node_event: NodeEvent, mac: str) -> None:
190209

191210
async def async_unload_entry(
192211
_hass: HomeAssistant,
193-
config_entry: ConfigEntry,
212+
config_entry: PlugwiseUSBConfigEntry,
194213
) -> None:
195214
"""Unload a config entry."""
196215
config_entry.runtime_data[Platform.SENSOR][UNSUB_NODE_LOADED]()

custom_components/plugwise_usb/strings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@
115115
"energy_day_consumption": {
116116
"name": "Energy consumption today"
117117
},
118+
"energy_hour_production": {
119+
"name": "Energy production this hour"
120+
},
121+
"energy_day_production": {
122+
"name": "Energy production today"
123+
},
118124
"last_seen": {
119125
"name": "Last seen"
120126
},

custom_components/plugwise_usb/switch.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
SwitchEntity,
1212
SwitchEntityDescription,
1313
)
14-
from homeassistant.config_entries import ConfigEntry
1514
from homeassistant.const import EntityCategory, Platform
1615
from homeassistant.core import HomeAssistant, callback
1716
from homeassistant.helpers.entity_platform import AddEntitiesCallback
1817
from plugwise_usb.api import NodeEvent, NodeFeature
1918

2019
from .const import NODES, STICK, UNSUB_NODE_LOADED
21-
from .coordinator import PlugwiseUSBDataUpdateCoordinator
20+
from .coordinator import PlugwiseUSBConfigEntry, PlugwiseUSBDataUpdateCoordinator
2221
from .entity import PlugwiseUSBEntity, PlugwiseUSBEntityDescription
2322

2423
_LOGGER = logging.getLogger(__name__)
@@ -66,7 +65,7 @@ class PlugwiseSwitchEntityDescription(
6665

6766
async def async_setup_entry(
6867
_hass: HomeAssistant,
69-
config_entry: ConfigEntry,
68+
config_entry: PlugwiseUSBConfigEntry,
7069
async_add_entities: AddEntitiesCallback,
7170
) -> None:
7271
"""Set up the USB switches from a config entry."""
@@ -107,7 +106,7 @@ async def async_add_switch(node_event: NodeEvent, mac: str) -> None:
107106

108107
async def async_unload_entry(
109108
_hass: HomeAssistant,
110-
config_entry: ConfigEntry,
109+
config_entry: PlugwiseUSBConfigEntry,
111110
) -> None:
112111
"""Unload a config entry."""
113112
config_entry.runtime_data[Platform.SWITCH][UNSUB_NODE_LOADED]()

0 commit comments

Comments
 (0)