Skip to content

Commit 32ad10a

Browse files
committed
Adapt relevant code
1 parent fa724ed commit 32ad10a

File tree

10 files changed

+157
-89
lines changed

10 files changed

+157
-89
lines changed

custom_components/plugwise/binary_sensor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def is_on(self) -> bool:
174174
self.hass, message, "Plugwise Notification:", f"{DOMAIN}.{notify_id}"
175175
)
176176

177-
return self.device[BINARY_SENSORS][self.entity_description.key]
177+
return self.device_or_zone[BINARY_SENSORS][self.entity_description.key]
178178

179179
@property
180180
def extra_state_attributes(self) -> Mapping[str, Any] | None:

custom_components/plugwise/climate.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ def _add_entities() -> None:
9090
)
9191
LOGGER.debug("Add climate %s", device[ATTR_NAME])
9292

93+
for device_id in coordinator.new_zones:
94+
thermostat = coordinator.data.zones[device_id]
95+
entities.append(
96+
PlugwiseClimateEntity(
97+
coordinator, device_id, homekit_enabled
98+
) # pw-beta homekit emulation
99+
)
100+
LOGGER.debug("Add climate %s", thermostat[ATTR_NAME])
101+
93102
async_add_entities(entities)
94103

95104
_add_entities()
@@ -118,14 +127,18 @@ def __init__(
118127
super().__init__(coordinator, device_id)
119128

120129
self._homekit_enabled = homekit_enabled # pw-beta homekit emulation
130+
self._location = device_id
131+
if (location := self.device_or_zone.get(LOCATION)) is not None:
132+
self._location = location
133+
121134
gateway_id: str = coordinator.data.gateway[GATEWAY_ID]
122135
self.gateway_data = coordinator.data.devices[gateway_id]
123136

124-
self._attr_max_temp = min(self.device[THERMOSTAT][UPPER_BOUND], 35.0)
125-
self._attr_min_temp = self.device[THERMOSTAT][LOWER_BOUND]
137+
self._attr_max_temp = min(self.device_or_zone[THERMOSTAT][UPPER_BOUND], 35.0)
138+
self._attr_min_temp = self.device_or_zone[THERMOSTAT][LOWER_BOUND]
126139
# Ensure we don't drop below 0.1
127140
self._attr_target_temperature_step = max(
128-
self.device[THERMOSTAT][RESOLUTION], 0.1
141+
self.device_or_zone[THERMOSTAT][RESOLUTION], 0.1
129142
)
130143
self._attr_unique_id = f"{device_id}-climate"
131144

@@ -143,7 +156,7 @@ def __init__(
143156
self._attr_supported_features |= (
144157
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
145158
)
146-
if presets := self.device["preset_modes"]: # can be NONE
159+
if presets := self.device_or_zone["preset_modes"]: # can be NONE
147160
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
148161
self._attr_preset_modes = presets
149162

@@ -164,7 +177,7 @@ def _previous_action_mode(self, coordinator: PlugwiseDataUpdateCoordinator) -> N
164177
@property
165178
def current_temperature(self) -> float:
166179
"""Return the current temperature."""
167-
return self.device[SENSORS][ATTR_TEMPERATURE]
180+
return self.device_or_zone[SENSORS][ATTR_TEMPERATURE]
168181

169182
@property
170183
def target_temperature(self) -> float:
@@ -173,29 +186,29 @@ def target_temperature(self) -> float:
173186
Connected to the HVACMode combination of AUTO-HEAT.
174187
"""
175188

176-
return self.device[THERMOSTAT][TARGET_TEMP]
189+
return self.device_or_zone[THERMOSTAT][TARGET_TEMP]
177190

178191
@property
179192
def target_temperature_high(self) -> float:
180193
"""Return the temperature we try to reach in case of cooling.
181194
182195
Connected to the HVACMode combination of AUTO-HEAT_COOL.
183196
"""
184-
return self.device[THERMOSTAT][TARGET_TEMP_HIGH]
197+
return self.device_or_zone[THERMOSTAT][TARGET_TEMP_HIGH]
185198

186199
@property
187200
def target_temperature_low(self) -> float:
188201
"""Return the heating temperature we try to reach in case of heating.
189202
190203
Connected to the HVACMode combination AUTO-HEAT_COOL.
191204
"""
192-
return self.device[THERMOSTAT][TARGET_TEMP_LOW]
205+
return self.device_or_zone[THERMOSTAT][TARGET_TEMP_LOW]
193206

194207
@property
195208
def hvac_mode(self) -> HVACMode:
196209
"""Return HVAC operation ie. auto, cool, heat, heat_cool, or off mode."""
197210
if (
198-
mode := self.device[CLIMATE_MODE]
211+
mode := self.device_or_zone[CLIMATE_MODE]
199212
) is None or mode not in self.hvac_modes: # pw-beta add to Core
200213
return HVACMode.HEAT # pragma: no cover
201214
# pw-beta homekit emulation
@@ -214,7 +227,7 @@ def hvac_modes(self) -> list[HVACMode]:
214227
):
215228
hvac_modes.append(HVACMode.OFF)
216229

217-
if AVAILABLE_SCHEDULES in self.device:
230+
if AVAILABLE_SCHEDULES in self.device_or_zone:
218231
hvac_modes.append(HVACMode.AUTO)
219232

220233
if self.cdr_gateway[COOLING_PRESENT]:
@@ -237,14 +250,14 @@ def hvac_action(self) -> HVACAction: # pw-beta add to Core
237250
self._previous_action_mode(self.coordinator)
238251

239252
# Adam provides the hvac_action for each thermostat
240-
if (control_state := self.device.get(CONTROL_STATE)) in (HVACAction.COOLING, HVACAction.HEATING, HVACAction.PREHEATING):
253+
if (control_state := self.device_or_zone.get(CONTROL_STATE)) in (HVACAction.COOLING, HVACAction.HEATING, HVACAction.PREHEATING):
241254
return cast(HVACAction, control_state)
242255
if control_state == HVACMode.OFF:
243256
return HVACAction.IDLE
244257

245258
# Anna
246259
heater: str = self.coordinator.data.gateway["heater_id"]
247-
heater_data = self.coordinator.data.devices[heater]
260+
heater_data = self.coordinator.data.device_or_zone[heater]
248261
if heater_data[BINARY_SENSORS][HEATING_STATE]:
249262
return HVACAction.HEATING
250263
if heater_data[BINARY_SENSORS].get(COOLING_STATE, False):
@@ -255,7 +268,7 @@ def hvac_action(self) -> HVACAction: # pw-beta add to Core
255268
@property
256269
def preset_mode(self) -> str | None:
257270
"""Return the current preset mode."""
258-
return self.device[ACTIVE_PRESET]
271+
return self.device_or_zone[ACTIVE_PRESET]
259272

260273
@plugwise_command
261274
async def async_set_temperature(self, **kwargs: Any) -> None:
@@ -273,7 +286,7 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
273286
if mode := kwargs.get(ATTR_HVAC_MODE):
274287
await self.async_set_hvac_mode(mode)
275288

276-
await self.coordinator.api.set_temperature(self.device[LOCATION], data)
289+
await self.coordinator.api.set_temperature(self._location, data)
277290

278291
@plugwise_command
279292
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
@@ -286,7 +299,7 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
286299

287300
if hvac_mode != HVACMode.OFF:
288301
await self.coordinator.api.set_schedule_state(
289-
self.device[LOCATION],
302+
self._location,
290303
STATE_ON if hvac_mode == HVACMode.AUTO else STATE_OFF,
291304
)
292305

@@ -303,11 +316,11 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
303316
await self.async_set_preset_mode(PRESET_AWAY) # pragma: no cover
304317
if (
305318
self._homekit_mode in [HVACMode.HEAT, HVACMode.HEAT_COOL]
306-
and self.device[ACTIVE_PRESET] == PRESET_AWAY
319+
and self.device_or_zone[ACTIVE_PRESET] == PRESET_AWAY
307320
): # pragma: no cover
308321
await self.async_set_preset_mode(PRESET_HOME) # pragma: no cover
309322

310323
@plugwise_command
311324
async def async_set_preset_mode(self, preset_mode: str) -> None:
312325
"""Set the preset mode."""
313-
await self.coordinator.api.set_preset(self.device[LOCATION], preset_mode)
326+
await self.coordinator.api.set_preset(self._location, preset_mode)

custom_components/plugwise/coordinator.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ def __init__(
7070
websession=async_get_clientsession(hass, verify_ssl=False),
7171
)
7272
self._current_devices: set[str] = set()
73+
self._current_zones: set[str] = set()
7374
self.new_devices: set[str] = set()
75+
self.new_zones: set[str] = set()
7476
self.update_interval = update_interval
7577

7678
async def _connect(self) -> None:
@@ -91,7 +93,7 @@ async def _connect(self) -> None:
9193

9294
async def _async_update_data(self) -> PlugwiseData:
9395
"""Fetch data from Plugwise."""
94-
data = PlugwiseData({}, {})
96+
data = PlugwiseData({}, {}, {})
9597
try:
9698
if not self._connected:
9799
await self._connect()
@@ -110,19 +112,24 @@ async def _async_update_data(self) -> PlugwiseData:
110112
raise ConfigEntryError("Device with unsupported firmware") from err
111113
else:
112114
LOGGER.debug(f"{self.api.smile_name} data: %s", data)
113-
await self.async_add_remove_devices(data, self.config_entry)
115+
await self.async_add_remove_devices_zones(data, self.config_entry)
114116

115117
return data
116118

117-
async def async_add_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
119+
async def async_add_remove_devices_zones(self, data: PlugwiseData, entry: ConfigEntry) -> None:
118120
"""Add new Plugwise devices, remove non-existing devices."""
119121
# Check for new or removed devices
120122
self.new_devices = set(data.devices) - self._current_devices
123+
self.new_zones = set(data.zones) - self._current_zones
121124
removed_devices = self._current_devices - set(data.devices)
125+
removed_zones = self._current_zones - set(data.zones)
122126
self._current_devices = set(data.devices)
127+
self._current_zones = set(data.zones)
123128

124129
if removed_devices:
125130
await self.async_remove_devices(data, entry)
131+
# if removed_zones:
132+
# await self.async_remove_zones(data, entry)
126133

127134
async def async_remove_devices(self, data: PlugwiseData, entry: ConfigEntry) -> None:
128135
"""Clean registries when removed devices found."""

custom_components/plugwise/diagnostics.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ async def async_get_config_entry_diagnostics(
1717
return {
1818
"gateway": coordinator.data.gateway,
1919
"devices": coordinator.data.devices,
20+
"zones": coordinator.data.zones,
2021
}

custom_components/plugwise/entity.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from plugwise.constants import DeviceData
5+
from plugwise.constants import DeviceZoneData
66

77
from homeassistant.const import ATTR_NAME, ATTR_VIA_DEVICE, CONF_HOST
88
from homeassistant.helpers.device_registry import (
@@ -48,51 +48,54 @@ def __init__(
4848
if entry := self.coordinator.config_entry:
4949
configuration_url = f"http://{entry.data[CONF_HOST]}"
5050

51-
data = coordinator.data.devices[device_id]
52-
connections = set()
53-
if mac := data.get(MAC_ADDRESS):
54-
connections.add((CONNECTION_NETWORK_MAC, mac))
55-
if mac := data.get(ZIGBEE_MAC_ADDRESS):
56-
connections.add((CONNECTION_ZIGBEE, mac))
57-
58-
self._attr_device_info = DeviceInfo(
59-
configuration_url=configuration_url,
60-
identifiers={(DOMAIN, device_id)},
61-
connections=connections,
62-
manufacturer=data.get(VENDOR),
63-
model=data.get(MODEL),
64-
model_id=data.get(MODEL_ID),
65-
name=coordinator.data.gateway[SMILE_NAME],
66-
sw_version=data.get(FIRMWARE),
67-
hw_version=data.get(HARDWARE),
68-
)
69-
70-
if device_id != coordinator.data.gateway[GATEWAY_ID]:
71-
self._attr_device_info.update(
72-
{
73-
ATTR_NAME: data.get(ATTR_NAME),
74-
ATTR_VIA_DEVICE: (
75-
DOMAIN,
76-
str(self.coordinator.data.gateway[GATEWAY_ID]),
77-
),
78-
}
51+
if (data := coordinator.data.devices.get(device_id)) is not None:
52+
connections = set()
53+
if mac := data.get(MAC_ADDRESS):
54+
connections.add((CONNECTION_NETWORK_MAC, mac))
55+
if mac := data.get(ZIGBEE_MAC_ADDRESS):
56+
connections.add((CONNECTION_ZIGBEE, mac))
57+
58+
self._attr_device_info = DeviceInfo(
59+
configuration_url=configuration_url,
60+
identifiers={(DOMAIN, device_id)},
61+
connections=connections,
62+
manufacturer=data.get(VENDOR),
63+
model=data.get(MODEL),
64+
model_id=data.get(MODEL_ID),
65+
name=coordinator.data.gateway[SMILE_NAME],
66+
sw_version=data.get(FIRMWARE),
67+
hw_version=data.get(HARDWARE),
7968
)
8069

70+
if device_id != coordinator.data.gateway[GATEWAY_ID]:
71+
self._attr_device_info.update(
72+
{
73+
ATTR_NAME: data.get(ATTR_NAME),
74+
ATTR_VIA_DEVICE: (
75+
DOMAIN,
76+
str(self.coordinator.data.gateway[GATEWAY_ID]),
77+
),
78+
}
79+
)
80+
8181
@property
8282
def available(self) -> bool:
8383
"""Return if entity is available."""
8484
return (
8585
# Upstream: Do not change the AVAILABLE line below: some Plugwise devices
8686
# Upstream: do not provide their availability-status!
8787
self._dev_id in self.coordinator.data.devices
88-
and (AVAILABLE not in self.device or self.device[AVAILABLE] is True)
88+
and (AVAILABLE not in self.device_or_zone or self.device_or_zone[AVAILABLE] is True)
8989
and super().available
9090
)
9191

9292
@property
93-
def device(self) -> DeviceData:
94-
"""Return data for this device."""
95-
return self.coordinator.data.devices[self._dev_id]
93+
def device_or_zone(self) -> DeviceZoneData:
94+
"""Return the device or zone connected to the dev_id."""
95+
if (device := self.coordinator.data.devices[self._dev_id]) is not None:
96+
return device
97+
98+
return self.coordinator.data.zones[self._dev_id]
9699

97100
async def async_added_to_hass(self) -> None:
98101
"""Subscribe to updates."""

custom_components/plugwise/number.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,23 @@ def __init__(
122122
) -> None:
123123
"""Initiate Plugwise Number."""
124124
super().__init__(coordinator, device_id)
125-
self.actuator = self.device[description.key] # Upstream
125+
self.actuator = self.device_or_zone[description.key] # Upstream
126126
self.device_id = device_id
127127
self.entity_description = description
128128
self._attr_unique_id = f"{device_id}-{description.key}"
129129
self._attr_mode = NumberMode.BOX
130-
self._attr_native_max_value = self.device[description.key][UPPER_BOUND] # Upstream const
131-
self._attr_native_min_value = self.device[description.key][LOWER_BOUND] # Upstream const
130+
self._attr_native_max_value = self.device_or_zone[description.key][UPPER_BOUND] # Upstream const
131+
self._attr_native_min_value = self.device_or_zone[description.key][LOWER_BOUND] # Upstream const
132132

133-
native_step = self.device[description.key][RESOLUTION] # Upstream const
133+
native_step = self.device_or_zone[description.key][RESOLUTION] # Upstream const
134134
if description.key != TEMPERATURE_OFFSET: # Upstream const
135135
native_step = max(native_step, 0.5)
136136
self._attr_native_step = native_step
137137

138138
@property
139139
def native_value(self) -> float:
140140
"""Return the present setpoint value."""
141-
return self.device[self.entity_description.key]["setpoint"]
141+
return self.device_or_zone[self.entity_description.key]["setpoint"]
142142

143143
@plugwise_command
144144
async def async_set_native_value(self, value: float) -> None:

custom_components/plugwise/select.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,30 @@ def __init__(
124124
) -> None:
125125
"""Initialise the selector."""
126126
super().__init__(coordinator, device_id)
127-
self.entity_description = entity_description
128127
self._attr_unique_id = f"{device_id}-{entity_description.key}"
128+
self.entity_description = entity_description
129+
self._location = device_id
130+
if (location := self.device.get(LOCATION)) is not None:
131+
self._location = location
129132

130133
@property
131134
def current_option(self) -> str:
132135
"""Return the selected entity option to represent the entity state."""
133-
return self.device[self.entity_description.key]
136+
return self.device_or_zone[self.entity_description.key]
134137

135138
@property
136139
def options(self) -> list[str]:
137140
"""Return the available select-options."""
138-
return self.device[self.entity_description.options_key]
141+
return self.device_or_zone[self.entity_description.options_key]
139142

140143
@plugwise_command
141144
async def async_select_option(self, option: str) -> None:
142145
"""Change to the selected entity option.
143146
144-
self.device[LOCATION] and STATE_ON are required for the thermostat-schedule select.
147+
Location ID and STATE_ON are required for the thermostat-schedule select.
145148
"""
146149
await self.coordinator.api.set_select(
147-
self.entity_description.key, self.device[LOCATION], option, STATE_ON
150+
self.entity_description.key, self._location, option, STATE_ON
148151
)
149152
LOGGER.debug(
150153
"Set %s to %s was successful",

0 commit comments

Comments
 (0)