Skip to content

Commit 1a62d80

Browse files
authored
Merge pull request #671 from plugwise/rework-runtime-update-2
Refactor runtime-update/remove related code
2 parents fec5085 + f1c8200 commit 1a62d80

File tree

11 files changed

+99
-117
lines changed

11 files changed

+99
-117
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
## Versions from 0.40 and up
44

5-
## Ongoing
5+
## v0.51.4
66

7+
- Refactor runtime-update/remove related code.
78
- Implement various new Core features.
89

910
## v0.51.3

custom_components/plugwise/binary_sensor.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,33 +98,27 @@ async def async_setup_entry(
9898

9999
@callback
100100
def _add_entities() -> None:
101-
"""Add Entities."""
101+
"""Add Entities during init and runtime."""
102102
if not coordinator.new_devices:
103103
return
104104

105105
entities: list[PlugwiseBinarySensorEntity] = []
106-
for device_id, device in coordinator.data.devices.items():
106+
for device_id in coordinator.new_devices:
107+
device = coordinator.data.devices[device_id]
107108
if not (binary_sensors := device.get(BINARY_SENSORS)):
108109
continue
109110
for description in PLUGWISE_BINARY_SENSORS:
110111
if description.key not in binary_sensors:
111112
continue
112-
entities.append(
113-
PlugwiseBinarySensorEntity(
114-
coordinator,
115-
device_id,
116-
description,
117-
)
118-
)
113+
entities.append(PlugwiseBinarySensorEntity(coordinator, device_id, description))
119114
LOGGER.debug(
120115
"Add %s %s binary sensor", device[ATTR_NAME], description.translation_key
121116
)
122117

123118
async_add_entities(entities)
124119

125-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
126-
127120
_add_entities()
121+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
128122

129123

130124
class PlugwiseBinarySensorEntity(PlugwiseEntity, BinarySensorEntity):

custom_components/plugwise/button.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,25 @@ async def async_setup_entry(
3636

3737
@callback
3838
def _add_entities() -> None:
39-
"""Add Entities."""
39+
"""Add Entities during init and runtime."""
4040
if not coordinator.new_devices:
4141
return
4242

4343
entities: list[PlugwiseButtonEntity] = []
4444
gateway = coordinator.data.gateway
45-
for device_id, device in coordinator.data.devices.items():
45+
for device_id in coordinator.new_devices:
46+
device = coordinator.data.devices[device_id]
4647
if device_id == gateway[GATEWAY_ID] and REBOOT in gateway:
4748
for description in BUTTON_TYPES:
48-
entities.append(
49-
PlugwiseButtonEntity(
50-
coordinator,
51-
device_id,
52-
description,
53-
)
54-
)
49+
entities.append(PlugwiseButtonEntity(coordinator, device_id, description))
5550
LOGGER.debug(
5651
"Add %s %s button", device[ATTR_NAME], description.key
5752
)
5853

5954
async_add_entities(entities)
6055

61-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
62-
6356
_add_entities()
57+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
6458

6559

6660
class PlugwiseButtonEntity(PlugwiseEntity, ButtonEntity):

custom_components/plugwise/climate.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414
HVACAction,
1515
HVACMode,
1616
)
17-
from homeassistant.const import ATTR_TEMPERATURE, STATE_OFF, STATE_ON, UnitOfTemperature
17+
from homeassistant.const import (
18+
ATTR_NAME,
19+
ATTR_TEMPERATURE,
20+
STATE_OFF,
21+
STATE_ON,
22+
UnitOfTemperature,
23+
)
1824
from homeassistant.core import HomeAssistant, callback
1925
from homeassistant.exceptions import HomeAssistantError
2026
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@@ -33,6 +39,7 @@
3339
GATEWAY_ID,
3440
HEATING_STATE,
3541
LOCATION,
42+
LOGGER,
3643
LOWER_BOUND,
3744
MASTER_THERMOSTATS,
3845
MODE,
@@ -57,29 +64,33 @@ async def async_setup_entry(
5764
entry: PlugwiseConfigEntry,
5865
async_add_entities: AddEntitiesCallback,
5966
) -> None:
60-
"""Set up the Smile Thermostats from a ConfigEntry."""
67+
"""Set up the Plugwise thermostats from a ConfigEntry."""
6168
coordinator = entry.runtime_data
6269
homekit_enabled: bool = entry.options.get(
6370
CONF_HOMEKIT_EMULATION, False
6471
) # pw-beta homekit emulation
6572

6673
@callback
6774
def _add_entities() -> None:
68-
"""Add Entities."""
75+
"""Add Entities during init and runtime."""
6976
if not coordinator.new_devices:
7077
return
7178

72-
async_add_entities(
73-
PlugwiseClimateEntity(
74-
coordinator, device_id, homekit_enabled
75-
) # pw-beta homekit emulation
76-
for device_id, device in coordinator.data.devices.items()
77-
if device[DEV_CLASS] in MASTER_THERMOSTATS
78-
)
79+
entities: list[PlugwiseClimateEntity] = []
80+
for device_id in coordinator.new_devices:
81+
device = coordinator.data.devices[device_id]
82+
if device[DEV_CLASS] in MASTER_THERMOSTATS:
83+
entities.append(
84+
PlugwiseClimateEntity(
85+
coordinator, device_id, homekit_enabled
86+
) # pw-beta homekit emulation
87+
)
88+
LOGGER.debug("Add climate %s", device[ATTR_NAME])
7989

80-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
90+
async_add_entities(entities)
8191

8292
_add_entities()
93+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
8394

8495

8596
class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):

custom_components/plugwise/coordinator.py

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from homeassistant.helpers import device_registry as dr
2525
from homeassistant.helpers.aiohttp_client import async_get_clientsession
2626
from homeassistant.helpers.debounce import Debouncer
27-
from homeassistant.helpers.device_registry import DeviceEntry, DeviceRegistry
2827
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
2928

3029
from .const import (
@@ -37,45 +36,6 @@
3736
)
3837

3938

40-
async def cleanup_device_and_entity_registry(
41-
data: PlugwiseData,
42-
device_reg: DeviceRegistry,
43-
device_list: list[DeviceEntry],
44-
entry: ConfigEntry,
45-
) -> None:
46-
"""Remove deleted devices from device- and entity-registry."""
47-
if len(device_list) - len(data.devices.keys()) <= 0:
48-
return
49-
50-
# via_device cannot be None, this will result in the deletion
51-
# of other Plugwise Gateways when present!
52-
via_device: str = ""
53-
for device_entry in device_list:
54-
if not device_entry.identifiers:
55-
continue # pragma: no cover
56-
57-
item = list(list(device_entry.identifiers)[0])
58-
if item[0] != DOMAIN:
59-
continue # pragma: no cover
60-
61-
# First find the Plugwise via_device, this is always the first device
62-
if item[1] == data.gateway[GATEWAY_ID]:
63-
via_device = device_entry.id
64-
elif ( # then remove the connected orphaned device(s)
65-
device_entry.via_device_id == via_device
66-
and item[1] not in list(data.devices.keys())
67-
):
68-
device_reg.async_update_device(
69-
device_entry.id, remove_config_entry_id=entry.entry_id
70-
)
71-
LOGGER.debug(
72-
"Removed %s device %s %s from device_registry",
73-
DOMAIN,
74-
device_entry.model,
75-
item[1],
76-
)
77-
78-
7939
class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]):
8040
"""Class to manage fetching Plugwise data from single endpoint."""
8141

@@ -114,9 +74,8 @@ def __init__(
11474
timeout=30,
11575
websession=async_get_clientsession(hass, verify_ssl=False),
11676
)
117-
self._unavailable_logged = False
118-
self.device_list: list[DeviceEntry] = []
119-
self.new_devices: bool = False
77+
self._current_devices: set[str] = set()
78+
self.new_devices: set[str] = set()
12079
self.update_interval = update_interval
12180

12281
async def _connect(self) -> None:
@@ -155,17 +114,46 @@ async def _async_update_data(self) -> PlugwiseData:
155114
raise ConfigEntryError("Device with unsupported firmware") from err
156115
else:
157116
LOGGER.debug(f"{self.api.smile_name} data: %s", data)
158-
device_reg = dr.async_get(self.hass)
159-
device_list = dr.async_entries_for_config_entry(
160-
device_reg, self.config_entry.entry_id
161-
)
162-
await cleanup_device_and_entity_registry(
163-
data,
164-
device_reg,
165-
device_list,
166-
self.config_entry
167-
)
168-
self.new_devices = len(data.devices.keys()) - len(self.device_list) > 0
169-
self.device_list = device_list
117+
self._async_add_remove_devices(data, self.config_entry)
170118

171119
return data
120+
121+
def _async_add_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
122+
"""Add new Plugwise devices, remove non-existing devices."""
123+
# Check for new or removed devices
124+
self.new_devices = set(data.devices) - self._current_devices
125+
removed_devices = self._current_devices - set(data.devices)
126+
self._current_devices = set(data.devices)
127+
128+
if removed_devices:
129+
self._async_remove_devices(data, entry)
130+
131+
def _async_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
132+
"""Clean registries when removed devices found."""
133+
device_reg = dr.async_get(self.hass)
134+
device_list = dr.async_entries_for_config_entry(
135+
device_reg, self.config_entry.entry_id
136+
)
137+
# via_device cannot be None, this will result in the deletion
138+
# of other Plugwise Gateways when present!
139+
via_device: str = ""
140+
for device_entry in device_list:
141+
if device_entry.identifiers:
142+
item = list(list(device_entry.identifiers)[0])
143+
if item[0] == DOMAIN:
144+
# First find the Plugwise via_device, this is always the first device
145+
if item[1] == data.gateway[GATEWAY_ID]:
146+
via_device = device_entry.id
147+
elif ( # then remove the connected orphaned device(s)
148+
device_entry.via_device_id == via_device
149+
and item[1] not in list(data.devices.keys())
150+
):
151+
device_reg.async_update_device(
152+
device_entry.id, remove_config_entry_id=entry.entry_id
153+
)
154+
LOGGER.debug(
155+
"Removed %s device %s %s from device_registry",
156+
DOMAIN,
157+
device_entry.model,
158+
item[1],
159+
)

custom_components/plugwise/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"],
1111
"requirements": ["plugwise==0.38.3"],
12-
"version": "0.51.3"
12+
"version": "0.51.4"
1313
}

custom_components/plugwise/number.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,13 @@ async def async_setup_entry(
7171

7272
@callback
7373
def _add_entities() -> None:
74-
"""Add Entities."""
74+
"""Add Entities during init and runtime."""
7575
if not coordinator.new_devices:
7676
return
7777

7878
entities: list[PlugwiseNumberEntity] = []
79-
for device_id, device in coordinator.data.devices.items():
79+
for device_id in coordinator.new_devices:
80+
device = coordinator.data.devices[device_id]
8081
for description in NUMBER_TYPES:
8182
if description.key in device:
8283
entities.append(
@@ -88,9 +89,8 @@ def _add_entities() -> None:
8889

8990
async_add_entities(entities)
9091

91-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
92-
9392
_add_entities()
93+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
9494

9595

9696
class PlugwiseNumberEntity(PlugwiseEntity, NumberEntity):

custom_components/plugwise/select.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,18 @@ async def async_setup_entry(
7373
entry: PlugwiseConfigEntry,
7474
async_add_entities: AddEntitiesCallback,
7575
) -> None:
76-
"""Set up the Smile selector from a ConfigEntry."""
76+
"""Set up the Plugwise selectors from a ConfigEntry."""
7777
coordinator = entry.runtime_data
7878

7979
@callback
8080
def _add_entities() -> None:
81-
"""Add Entities."""
81+
"""Add Entities during init and runtime."""
8282
if not coordinator.new_devices:
8383
return
8484

8585
entities: list[PlugwiseSelectEntity] = []
86-
for device_id, device in coordinator.data.devices.items():
86+
for device_id in coordinator.new_devices:
87+
device = coordinator.data.devices[device_id]
8788
for description in SELECT_TYPES:
8889
if description.options_key in device:
8990
entities.append(
@@ -95,9 +96,8 @@ def _add_entities() -> None:
9596

9697
async_add_entities(entities)
9798

98-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
99-
10099
_add_entities()
100+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
101101

102102

103103
class PlugwiseSelectEntity(PlugwiseEntity, SelectEntity):

custom_components/plugwise/sensor.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -458,38 +458,32 @@ async def async_setup_entry(
458458
entry: PlugwiseConfigEntry,
459459
async_add_entities: AddEntitiesCallback,
460460
) -> None:
461-
"""Set up the Smile sensors from a ConfigEntry."""
461+
"""Set up the Plugwise sensors from a ConfigEntry."""
462462
coordinator = entry.runtime_data
463463

464464
@callback
465465
def _add_entities() -> None:
466-
"""Add Entities."""
466+
"""Add Entities during init and runtime."""
467467
if not coordinator.new_devices:
468468
return
469469

470470
entities: list[PlugwiseSensorEntity] = []
471-
for device_id, device in coordinator.data.devices.items():
471+
for device_id in coordinator.new_devices:
472+
device = coordinator.data.devices[device_id]
472473
if not (sensors := device.get(SENSORS)):
473474
continue
474475
for description in PLUGWISE_SENSORS:
475476
if description.key not in sensors:
476477
continue
477-
entities.append(
478-
PlugwiseSensorEntity(
479-
coordinator,
480-
device_id,
481-
description,
482-
)
483-
)
478+
entities.append(PlugwiseSensorEntity(coordinator, device_id, description))
484479
LOGGER.debug(
485480
"Add %s %s sensor", device[ATTR_NAME], description.translation_key or description.key
486481
)
487482

488483
async_add_entities(entities)
489484

490-
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
491-
492485
_add_entities()
486+
entry.async_on_unload(coordinator.async_add_listener(_add_entities))
493487

494488

495489
class PlugwiseSensorEntity(PlugwiseEntity, SensorEntity):

0 commit comments

Comments
 (0)