Skip to content

Commit 1d96d2c

Browse files
authored
Merge pull request #384 from plugwise/optimize
Optimize and reorder code, for the Stretch prevent the creation of a switch-group with an orphaned switch.
2 parents 7a97239 + 7030bca commit 1d96d2c

File tree

6 files changed

+115
-135
lines changed

6 files changed

+115
-135
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# Changelog
22

3-
## Ongoing
3+
## v0.32.2 Continuous improvements, bugfix
44

55
- Extend p1v4_442_triple userdata to include a Plugwise notification, extending the related fixture which is used in PW-beta and Core Plugwise.
66
- Optimize and rearrange typing-related constants, implement and cleanup.
7+
- Optimize and reorder code, for the Stretch prevent the creation of a switch-group with an orphaned switch.
78

89
## v0.32.1 Improve typing, bugfix
910

fixtures/stretch_v31/all_data.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,6 @@
4848
"vendor": "Plugwise",
4949
"zigbee_mac_address": "ABCD012345670A07"
5050
},
51-
"71e1944f2a944b26ad73323e399efef0": {
52-
"dev_class": "switching",
53-
"members": ["5ca521ac179d468e91d772eeeb8a2117"],
54-
"model": "Switchgroup",
55-
"name": "Test",
56-
"switches": {
57-
"relay": true
58-
}
59-
},
6051
"aac7b735042c4832ac9ff33aae4f453b": {
6152
"dev_class": "dishwasher",
6253
"firmware": "2011-06-27T10:52:18+02:00",

plugwise/__init__.py

Lines changed: 59 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,23 @@ def update_for_cooling(self, device: DeviceData) -> DeviceData:
8787
def _all_device_data(self) -> None:
8888
"""Helper-function for get_all_devices().
8989
90-
Collect initial data for each device and add to self.gw_data and self.gw_devices.
90+
Collect data for each device and add to self.gw_data and self.gw_devices.
9191
"""
92-
for device_id, device in self._appl_data.items():
93-
self.gw_devices.update({device_id: device})
94-
92+
for device_id, device in self.gw_devices.items():
9593
data = self._get_device_data(device_id)
94+
device.update(data)
9695
# Add plugwise notification binary_sensor to the relevant gateway
9796
if device_id == self.gateway_id and (
9897
self._is_thermostat
9998
or (not self._smile_legacy and self.smile_type == "power")
10099
):
101-
data["binary_sensors"]["plugwise_notification"] = False
102-
103-
self.gw_devices[device_id].update(data)
100+
device["binary_sensors"]["plugwise_notification"] = False
104101

105102
# Update for cooling
106-
if self.gw_devices[device_id]["dev_class"] in ZONE_THERMOSTATS:
107-
self.update_for_cooling(self.gw_devices[device_id])
103+
if device["dev_class"] in ZONE_THERMOSTATS:
104+
self.update_for_cooling(device)
108105

109-
remove_empty_platform_dicts(self.gw_devices[device_id])
106+
remove_empty_platform_dicts(device)
110107

111108
self.gw_data.update(
112109
{"smile_name": self.smile_name, "gateway_id": self.gateway_id}
@@ -122,69 +119,50 @@ def get_all_devices(self) -> None:
122119
Run this functions once to gather the initial device configuration,
123120
then regularly run async_update() to refresh the device data.
124121
"""
125-
# Start by determining the system capabilities:
126-
# Find the connected heating/cooling device (heater_central), e.g. heat-pump or gas-fired heater
122+
# Gather all the devices and their initial data
123+
self._all_appliances()
127124
if self.smile_type == "thermostat":
128-
onoff_boiler: etree = self._domain_objects.find(
129-
"./module/protocols/onoff_boiler"
130-
)
131-
open_therm_boiler: etree = self._domain_objects.find(
132-
"./module/protocols/open_therm_boiler"
133-
)
134-
self._on_off_device = onoff_boiler is not None
135-
self._opentherm_device = open_therm_boiler is not None
136-
137-
# Determine the presence of special features
138-
locator_1 = "./gateway/features/cooling"
139-
locator_2 = "./gateway/features/elga_support"
140-
search = self._domain_objects
141-
if search.find(locator_1) is not None:
142-
self._cooling_present = True
143-
if search.find(locator_2) is not None:
144-
self._elga = True
145-
125+
self._scan_thermostats()
126+
# Collect a list of thermostats with offset-capability
146127
self.therms_with_offset_func = (
147128
self._get_appliances_with_offset_functionality()
148129
)
149130

150-
# Gather all the device and initial data
151-
self._scan_thermostats()
131+
# Collect switching- or pump-group data
132+
if group_data := self._get_group_switches():
133+
self.gw_devices.update(group_data)
152134

153-
if group_data := self._group_switches():
154-
self._appl_data.update(group_data)
155-
156-
# Collect data for each device via helper function
135+
# Collect the remaining data for all device
157136
self._all_device_data()
158137

159138
def _device_data_switching_group(
160-
self, details: DeviceData, device_data: DeviceData
139+
self, device: DeviceData, device_data: DeviceData
161140
) -> DeviceData:
162141
"""Helper-function for _get_device_data().
163142
164143
Determine switching group device data.
165144
"""
166-
if details["dev_class"] in SWITCH_GROUP_TYPES:
167-
counter = 0
168-
for member in details["members"]:
169-
member_data = self._get_appliance_data(member)
170-
if member_data["switches"].get("relay"):
171-
counter += 1
172-
173-
device_data["switches"]["relay"] = counter != 0
145+
if device["dev_class"] not in SWITCH_GROUP_TYPES:
146+
return device_data
174147

148+
counter = 0
149+
for member in device["members"]:
150+
if self.gw_devices[member]["switches"].get("relay"):
151+
counter += 1
152+
device_data["switches"]["relay"] = counter != 0
175153
return device_data
176154

177155
def _device_data_adam(
178-
self, details: DeviceData, device_data: DeviceData
156+
self, device: DeviceData, device_data: DeviceData
179157
) -> DeviceData:
180158
"""Helper-function for _get_device_data().
181159
182-
Determine Adam device data.
160+
Determine Adam heating-status for on-off heating via valves.
183161
"""
184162
# Indicate heating_state based on valves being open in case of city-provided heating
185163
if (
186164
self.smile_name == "Adam"
187-
and details.get("dev_class") == "heater_central"
165+
and device.get("dev_class") == "heater_central"
188166
and self._on_off_device
189167
and self._heating_valves() is not None
190168
):
@@ -193,13 +171,13 @@ def _device_data_adam(
193171
return device_data
194172

195173
def _device_data_climate(
196-
self, details: DeviceData, device_data: DeviceData
174+
self, device: DeviceData, device_data: DeviceData
197175
) -> DeviceData:
198176
"""Helper-function for _get_device_data().
199177
200178
Determine climate-control device data.
201179
"""
202-
loc_id = details["location"]
180+
loc_id = device["location"]
203181

204182
# Presets
205183
device_data["preset_modes"] = None
@@ -246,22 +224,22 @@ def _device_data_climate(
246224
return device_data
247225

248226
def _check_availability(
249-
self, details: DeviceData, device_data: DeviceData
227+
self, device: DeviceData, device_data: DeviceData
250228
) -> DeviceData:
251229
"""Helper-function for _get_device_data().
252230
253231
Provide availability status for the wired-commected devices.
254232
"""
255233
# OpenTherm device
256-
if details["dev_class"] == "heater_central" and details["name"] != "OnOff":
234+
if device["dev_class"] == "heater_central" and device["name"] != "OnOff":
257235
device_data["available"] = True
258236
for data in self._notifications.values():
259237
for msg in data.values():
260238
if "no OpenTherm communication" in msg:
261239
device_data["available"] = False
262240

263241
# Smartmeter
264-
if details["dev_class"] == "smartmeter":
242+
if device["dev_class"] == "smartmeter":
265243
device_data["available"] = True
266244
for data in self._notifications.values():
267245
for msg in data.values():
@@ -275,14 +253,10 @@ def _get_device_data(self, dev_id: str) -> DeviceData:
275253
276254
Provide device-data, based on Location ID (= dev_id), from APPLIANCES.
277255
"""
278-
details = self._appl_data[dev_id]
279-
device_data = self._get_appliance_data(dev_id)
280-
# Remove thermostat-dict for thermo_sensors
281-
if details["dev_class"] == "thermo_sensor":
282-
device_data.pop("thermostat")
283-
256+
device = self.gw_devices[dev_id]
257+
device_data = self._get_measurement_data(dev_id)
284258
# Generic
285-
if self.smile_type == "thermostat" and details["dev_class"] == "gateway":
259+
if self.smile_type == "thermostat" and device["dev_class"] == "gateway":
286260
# Adam & Anna: the Smile outdoor_temperature is present in DOMAIN_OBJECTS and LOCATIONS - under Home
287261
# The outdoor_temperature present in APPLIANCES is a local sensor connected to the active device
288262
outdoor_temperature = self._object_value(
@@ -296,30 +270,23 @@ def _get_device_data(self, dev_id: str) -> DeviceData:
296270
device_data["regulation_modes"] = self._reg_allowed_modes
297271

298272
# Show the allowed dhw_modes
299-
if details["dev_class"] == "heater_central" and self._dhw_allowed_modes:
273+
if device["dev_class"] == "heater_central" and self._dhw_allowed_modes:
300274
device_data["dhw_modes"] = self._dhw_allowed_modes
301275

302-
# Get P1 smartmeter data from LOCATIONS or MODULES
303-
if details["dev_class"] == "smartmeter":
304-
if not self._smile_legacy:
305-
device_data.update(self._power_data_from_location(details["location"]))
306-
else:
307-
device_data.update(self._power_data_from_modules())
308-
309276
# Check availability of non-legacy wired-connected devices
310277
if not self._smile_legacy:
311-
self._check_availability(details, device_data)
278+
self._check_availability(device, device_data)
312279

313280
# Switching groups data
314-
device_data = self._device_data_switching_group(details, device_data)
281+
device_data = self._device_data_switching_group(device, device_data)
315282
# Specific, not generic Adam data
316-
device_data = self._device_data_adam(details, device_data)
283+
device_data = self._device_data_adam(device, device_data)
317284
# No need to obtain thermostat data when the device is not a thermostat
318-
if details["dev_class"] not in ZONE_THERMOSTATS:
285+
if device["dev_class"] not in ZONE_THERMOSTATS:
319286
return device_data
320287

321288
# Thermostat data (presets, temperatures etc)
322-
device_data = self._device_data_climate(details, device_data)
289+
device_data = self._device_data_climate(device, device_data)
323290

324291
return device_data
325292

@@ -484,7 +451,25 @@ async def _smile_detect(self, result: etree, dsmrmain: etree) -> None:
484451
self._stretch_v2 = self.smile_version[1].major == 2
485452
self._stretch_v3 = self.smile_version[1].major == 3
486453

487-
self._is_thermostat = self.smile_type == "thermostat"
454+
if self.smile_type == "thermostat":
455+
self._is_thermostat = True
456+
# For Adam, Anna, determine the system capabilities:
457+
# Find the connected heating/cooling device (heater_central),
458+
# e.g. heat-pump or gas-fired heater
459+
onoff_boiler: etree = result.find("./module/protocols/onoff_boiler")
460+
open_therm_boiler: etree = result.find(
461+
"./module/protocols/open_therm_boiler"
462+
)
463+
self._on_off_device = onoff_boiler is not None
464+
self._opentherm_device = open_therm_boiler is not None
465+
466+
# Determine the presence of special features
467+
locator_1 = "./gateway/features/cooling"
468+
locator_2 = "./gateway/features/elga_support"
469+
if result.find(locator_1) is not None:
470+
self._cooling_present = True
471+
if result.find(locator_2) is not None:
472+
self._elga = True
488473

489474
async def _full_update_device(self) -> None:
490475
"""Perform a first fetch of all XML data, needed for initialization."""
@@ -542,7 +527,6 @@ async def async_update(self) -> PlugwiseData:
542527
data["binary_sensors"]["plugwise_notification"] = bool(
543528
self._notifications
544529
)
545-
546530
device.update(data)
547531

548532
# Update for cooling

0 commit comments

Comments
 (0)