plugwise 1.5.1a3__py3-none-any.whl → 1.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plugwise/__init__.py +10 -10
- plugwise/common.py +68 -60
- plugwise/constants.py +47 -34
- plugwise/data.py +95 -67
- plugwise/helper.py +199 -168
- plugwise/legacy/data.py +23 -23
- plugwise/legacy/helper.py +57 -58
- plugwise/legacy/smile.py +18 -16
- plugwise/smile.py +24 -21
- plugwise/util.py +7 -9
- {plugwise-1.5.1a3.dist-info → plugwise-1.6.0.dist-info}/METADATA +1 -1
- plugwise-1.6.0.dist-info/RECORD +17 -0
- {plugwise-1.5.1a3.dist-info → plugwise-1.6.0.dist-info}/WHEEL +1 -1
- plugwise-1.5.1a3.dist-info/RECORD +0 -17
- {plugwise-1.5.1a3.dist-info → plugwise-1.6.0.dist-info}/LICENSE +0 -0
- {plugwise-1.5.1a3.dist-info → plugwise-1.6.0.dist-info}/top_level.txt +0 -0
plugwise/legacy/data.py
CHANGED
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
# Dict as class
|
8
8
|
# Version detection
|
9
|
-
from plugwise.constants import NONE, OFF,
|
9
|
+
from plugwise.constants import NONE, OFF, GwEntityData
|
10
10
|
from plugwise.legacy.helper import SmileLegacyHelper
|
11
11
|
from plugwise.util import remove_empty_platform_dicts
|
12
12
|
|
@@ -18,12 +18,12 @@ class SmileLegacyData(SmileLegacyHelper):
|
|
18
18
|
"""Init."""
|
19
19
|
SmileLegacyHelper.__init__(self)
|
20
20
|
|
21
|
-
def
|
22
|
-
"""Helper-function for
|
21
|
+
def _all_entity_data(self) -> None:
|
22
|
+
"""Helper-function for get_all_gateway_entities().
|
23
23
|
|
24
|
-
Collect data for each
|
24
|
+
Collect data for each entity and add to self.gw_data and self.gw_entities.
|
25
25
|
"""
|
26
|
-
self.
|
26
|
+
self._update_gw_entities()
|
27
27
|
self.gw_data.update(
|
28
28
|
{
|
29
29
|
"gateway_id": self.gateway_id,
|
@@ -36,40 +36,40 @@ class SmileLegacyData(SmileLegacyHelper):
|
|
36
36
|
{"heater_id": self._heater_id, "cooling_present": False}
|
37
37
|
)
|
38
38
|
|
39
|
-
def
|
40
|
-
"""Helper-function for
|
39
|
+
def _update_gw_entities(self) -> None:
|
40
|
+
"""Helper-function for _all_entity_data() and async_update().
|
41
41
|
|
42
|
-
Collect data for each
|
42
|
+
Collect data for each entity and add to self.gw_entities.
|
43
43
|
"""
|
44
|
-
for
|
45
|
-
data = self.
|
46
|
-
|
47
|
-
remove_empty_platform_dicts(
|
44
|
+
for entity_id, entity in self.gw_entities.items():
|
45
|
+
data = self._get_entity_data(entity_id)
|
46
|
+
entity.update(data)
|
47
|
+
remove_empty_platform_dicts(entity)
|
48
48
|
|
49
|
-
def
|
50
|
-
"""Helper-function for
|
49
|
+
def _get_entity_data(self, entity_id: str) -> GwEntityData:
|
50
|
+
"""Helper-function for _all_entity_data() and async_update().
|
51
51
|
|
52
|
-
Provide
|
52
|
+
Provide entity-data, based on Location ID (= entity_id), from APPLIANCES.
|
53
53
|
"""
|
54
|
-
|
55
|
-
data = self._get_measurement_data(
|
54
|
+
entity = self.gw_entities[entity_id]
|
55
|
+
data = self._get_measurement_data(entity_id)
|
56
56
|
|
57
57
|
# Switching groups data
|
58
|
-
self.
|
58
|
+
self._entity_switching_group(entity, data)
|
59
59
|
|
60
60
|
# Skip obtaining data when not a thermostat
|
61
|
-
if
|
61
|
+
if entity["dev_class"] != "thermostat":
|
62
62
|
return data
|
63
63
|
|
64
64
|
# Thermostat data (presets, temperatures etc)
|
65
|
-
self.
|
65
|
+
self._climate_data(entity, data)
|
66
66
|
|
67
67
|
return data
|
68
68
|
|
69
|
-
def
|
70
|
-
"""Helper-function for
|
69
|
+
def _climate_data(self, entity: GwEntityData, data: GwEntityData) -> None:
|
70
|
+
"""Helper-function for _get_entity_data().
|
71
71
|
|
72
|
-
Determine climate-control
|
72
|
+
Determine climate-control entity data.
|
73
73
|
"""
|
74
74
|
# Presets
|
75
75
|
data["preset_modes"] = None
|
plugwise/legacy/helper.py
CHANGED
@@ -29,8 +29,8 @@ from plugwise.constants import (
|
|
29
29
|
ActuatorDataType,
|
30
30
|
ActuatorType,
|
31
31
|
ApplianceType,
|
32
|
-
DeviceData,
|
33
32
|
GatewayData,
|
33
|
+
GwEntityData,
|
34
34
|
SensorType,
|
35
35
|
ThermoLoc,
|
36
36
|
)
|
@@ -68,6 +68,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
68
68
|
self._home_location: str
|
69
69
|
self._is_thermostat: bool
|
70
70
|
self._last_modified: dict[str, str] = {}
|
71
|
+
self._loc_data: dict[str, ThermoLoc]
|
71
72
|
self._locations: etree
|
72
73
|
self._modules: etree
|
73
74
|
self._notifications: dict[str, dict[str, str]] = {}
|
@@ -80,8 +81,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
80
81
|
|
81
82
|
self.gateway_id: str
|
82
83
|
self.gw_data: GatewayData = {}
|
83
|
-
self.
|
84
|
-
self.loc_data: dict[str, ThermoLoc]
|
84
|
+
self.gw_entities: dict[str, GwEntityData] = {}
|
85
85
|
self.smile_fw_version: Version | None
|
86
86
|
self.smile_hw_version: str | None
|
87
87
|
self.smile_mac_address: str | None
|
@@ -115,7 +115,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
115
115
|
continue # pragma: no cover
|
116
116
|
|
117
117
|
appl.location = self._home_location
|
118
|
-
appl.
|
118
|
+
appl.entity_id = appliance.attrib["id"]
|
119
119
|
appl.name = appliance.find("name").text
|
120
120
|
# Extend device_class name when a Circle/Stealth is type heater_central -- Pw-Beta Issue #739
|
121
121
|
if (
|
@@ -125,6 +125,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
125
125
|
appl.pwclass = "heater_central_plug"
|
126
126
|
|
127
127
|
appl.model = appl.pwclass.replace("_", " ").title()
|
128
|
+
appl.available = None
|
128
129
|
appl.model_id = None
|
129
130
|
appl.firmware = None
|
130
131
|
appl.hardware = None
|
@@ -138,20 +139,20 @@ class SmileLegacyHelper(SmileCommon):
|
|
138
139
|
continue
|
139
140
|
|
140
141
|
# Skip orphaned heater_central (Core Issue #104433)
|
141
|
-
if appl.pwclass == "heater_central" and appl.
|
142
|
+
if appl.pwclass == "heater_central" and appl.entity_id != self._heater_id:
|
142
143
|
continue # pragma: no cover
|
143
144
|
|
144
|
-
self.
|
145
|
+
self._create_gw_entities(appl)
|
145
146
|
|
146
147
|
# Place the gateway and optional heater_central devices as 1st and 2nd
|
147
148
|
for dev_class in ("heater_central", "gateway"):
|
148
|
-
for
|
149
|
-
if
|
150
|
-
|
151
|
-
self.
|
152
|
-
cleared_dict = self.
|
153
|
-
add_to_front = {
|
154
|
-
self.
|
149
|
+
for entity_id, entity in dict(self.gw_entities).items():
|
150
|
+
if entity["dev_class"] == dev_class:
|
151
|
+
tmp_entity = entity
|
152
|
+
self.gw_entities.pop(entity_id)
|
153
|
+
cleared_dict = self.gw_entities
|
154
|
+
add_to_front = {entity_id: tmp_entity}
|
155
|
+
self.gw_entities = {**add_to_front, **cleared_dict}
|
155
156
|
|
156
157
|
def _all_locations(self) -> None:
|
157
158
|
"""Collect all locations."""
|
@@ -160,7 +161,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
160
161
|
# Legacy Anna without outdoor_temp and Stretches have no locations, create fake location-data
|
161
162
|
if not (locations := self._locations.findall("./location")):
|
162
163
|
self._home_location = FAKE_LOC
|
163
|
-
self.
|
164
|
+
self._loc_data[FAKE_LOC] = {"name": "Home"}
|
164
165
|
return
|
165
166
|
|
166
167
|
for location in locations:
|
@@ -181,18 +182,18 @@ class SmileLegacyHelper(SmileCommon):
|
|
181
182
|
loc.name = "Home"
|
182
183
|
self._home_location = loc.loc_id
|
183
184
|
|
184
|
-
self.
|
185
|
+
self._loc_data[loc.loc_id] = {"name": loc.name}
|
185
186
|
|
186
187
|
def _create_legacy_gateway(self) -> None:
|
187
|
-
"""Create the (missing) gateway
|
188
|
+
"""Create the (missing) gateway entities for legacy Anna, P1 and Stretch.
|
188
189
|
|
189
|
-
Use the home_location or FAKE_APPL as
|
190
|
+
Use the home_location or FAKE_APPL as entity id.
|
190
191
|
"""
|
191
192
|
self.gateway_id = self._home_location
|
192
193
|
if self.smile_type == "power":
|
193
194
|
self.gateway_id = FAKE_APPL
|
194
195
|
|
195
|
-
self.
|
196
|
+
self.gw_entities[self.gateway_id] = {"dev_class": "gateway"}
|
196
197
|
self._count += 1
|
197
198
|
for key, value in {
|
198
199
|
"firmware": str(self.smile_fw_version),
|
@@ -205,34 +206,32 @@ class SmileLegacyHelper(SmileCommon):
|
|
205
206
|
}.items():
|
206
207
|
if value is not None:
|
207
208
|
gw_key = cast(ApplianceType, key)
|
208
|
-
self.
|
209
|
+
self.gw_entities[self.gateway_id][gw_key] = value
|
209
210
|
self._count += 1
|
210
211
|
|
211
212
|
def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
|
212
|
-
"""Collect
|
213
|
+
"""Collect entity info (Smile/Stretch, Thermostats, OpenTherm/On-Off): firmware, model and vendor name."""
|
213
214
|
match appl.pwclass:
|
214
|
-
# Collect thermostat
|
215
|
+
# Collect thermostat entity info
|
215
216
|
case _ as dev_class if dev_class in THERMOSTAT_CLASSES:
|
216
217
|
return self._appl_thermostat_info(appl, appliance, self._modules)
|
217
|
-
# Collect heater_central
|
218
|
+
# Collect heater_central entity info
|
218
219
|
case "heater_central":
|
219
220
|
return self._appl_heater_central_info(
|
220
221
|
appl, appliance, True, self._appliances, self._modules
|
221
222
|
) # True means legacy device
|
222
223
|
# Collect info from Stretches
|
223
224
|
case _:
|
224
|
-
return self.
|
225
|
+
return self._energy_entity_info_finder(appliance, appl)
|
225
226
|
|
226
|
-
def
|
227
|
+
def _energy_entity_info_finder(self, appliance: etree, appl: Munch) -> Munch:
|
227
228
|
"""Helper-function for _appliance_info_finder().
|
228
229
|
|
229
|
-
Collect energy
|
230
|
+
Collect energy entity info (Smartmeter, Circle, Stealth, etc.): firmware, model and vendor name.
|
230
231
|
"""
|
231
232
|
if self.smile_type in ("power", "stretch"):
|
232
233
|
locator = "./services/electricity_point_meter"
|
233
|
-
|
234
|
-
|
235
|
-
module_data = self._get_module_data(appliance, locator, mod_type, self._modules, legacy=True)
|
234
|
+
module_data = self._get_module_data(appliance, locator, self._modules, legacy=True)
|
236
235
|
appl.zigbee_mac = module_data["zigbee_mac_address"]
|
237
236
|
# Filter appliance without zigbee_mac, it's an orphaned device
|
238
237
|
if appl.zigbee_mac is None and self.smile_type != "power":
|
@@ -252,8 +251,9 @@ class SmileLegacyHelper(SmileCommon):
|
|
252
251
|
|
253
252
|
def _p1_smartmeter_info_finder(self, appl: Munch) -> None:
|
254
253
|
"""Collect P1 DSMR Smartmeter info."""
|
255
|
-
loc_id = next(iter(self.
|
256
|
-
appl.
|
254
|
+
loc_id = next(iter(self._loc_data.keys()))
|
255
|
+
appl.available = None
|
256
|
+
appl.entity_id = loc_id
|
257
257
|
appl.location = loc_id
|
258
258
|
appl.mac = None
|
259
259
|
appl.model = self.smile_model
|
@@ -262,41 +262,41 @@ class SmileLegacyHelper(SmileCommon):
|
|
262
262
|
appl.pwclass = "smartmeter"
|
263
263
|
appl.zigbee_mac = None
|
264
264
|
location = self._locations.find(f'./location[@id="{loc_id}"]')
|
265
|
-
appl = self.
|
265
|
+
appl = self._energy_entity_info_finder(location, appl)
|
266
266
|
|
267
|
-
self.
|
267
|
+
self._create_gw_entities(appl)
|
268
268
|
|
269
|
-
def _get_measurement_data(self,
|
270
|
-
"""Helper-function for smile.py:
|
269
|
+
def _get_measurement_data(self, entity_id: str) -> GwEntityData:
|
270
|
+
"""Helper-function for smile.py: _get_entity_data().
|
271
271
|
|
272
|
-
Collect the appliance-data based on
|
272
|
+
Collect the appliance-data based on entity_id.
|
273
273
|
"""
|
274
|
-
data:
|
274
|
+
data: GwEntityData = {"binary_sensors": {}, "sensors": {}, "switches": {}}
|
275
275
|
# Get P1 smartmeter data from LOCATIONS or MODULES
|
276
|
-
|
276
|
+
entity = self.gw_entities[entity_id]
|
277
277
|
# !! DON'T CHANGE below two if-lines, will break stuff !!
|
278
278
|
if self.smile_type == "power":
|
279
|
-
if
|
279
|
+
if entity["dev_class"] == "smartmeter":
|
280
280
|
data.update(self._power_data_from_modules())
|
281
281
|
|
282
282
|
return data
|
283
283
|
|
284
284
|
measurements = DEVICE_MEASUREMENTS
|
285
|
-
if self._is_thermostat and
|
285
|
+
if self._is_thermostat and entity_id == self._heater_id:
|
286
286
|
measurements = HEATER_CENTRAL_MEASUREMENTS
|
287
287
|
|
288
288
|
if (
|
289
|
-
appliance := self._appliances.find(f'./appliance[@id="{
|
289
|
+
appliance := self._appliances.find(f'./appliance[@id="{entity_id}"]')
|
290
290
|
) is not None:
|
291
291
|
self._appliance_measurements(appliance, data, measurements)
|
292
292
|
self._get_lock_state(appliance, data, self._stretch_v2)
|
293
293
|
|
294
294
|
if appliance.find("type").text in ACTUATOR_CLASSES:
|
295
|
-
self._get_actuator_functionalities(appliance,
|
295
|
+
self._get_actuator_functionalities(appliance, entity, data)
|
296
296
|
|
297
297
|
# Adam & Anna: the Smile outdoor_temperature is present in DOMAIN_OBJECTS and LOCATIONS - under Home
|
298
298
|
# The outdoor_temperature present in APPLIANCES is a local sensor connected to the active device
|
299
|
-
if self._is_thermostat and
|
299
|
+
if self._is_thermostat and entity_id == self.gateway_id:
|
300
300
|
outdoor_temperature = self._object_value(
|
301
301
|
self._home_location, "outdoor_temperature"
|
302
302
|
)
|
@@ -310,12 +310,12 @@ class SmileLegacyHelper(SmileCommon):
|
|
310
310
|
|
311
311
|
return data
|
312
312
|
|
313
|
-
def _power_data_from_modules(self) ->
|
314
|
-
"""Helper-function for smile.py:
|
313
|
+
def _power_data_from_modules(self) -> GwEntityData:
|
314
|
+
"""Helper-function for smile.py: _get_entity_data().
|
315
315
|
|
316
316
|
Collect the power-data from MODULES (P1 legacy only).
|
317
317
|
"""
|
318
|
-
|
318
|
+
data: GwEntityData = {"sensors": {}}
|
319
319
|
loc = Munch()
|
320
320
|
mod_list: list[str] = ["interval_meter", "cumulative_meter", "point_meter"]
|
321
321
|
t_string = "tariff_indicator"
|
@@ -326,15 +326,15 @@ class SmileLegacyHelper(SmileCommon):
|
|
326
326
|
loc.meas_list = loc.measurement.split("_")
|
327
327
|
for loc.logs in mod_logs:
|
328
328
|
for loc.log_type in mod_list:
|
329
|
-
self._collect_power_values(
|
329
|
+
self._collect_power_values(data, loc, t_string, legacy=True)
|
330
330
|
|
331
|
-
self._count += len(
|
332
|
-
return
|
331
|
+
self._count += len(data["sensors"])
|
332
|
+
return data
|
333
333
|
|
334
334
|
def _appliance_measurements(
|
335
335
|
self,
|
336
336
|
appliance: etree,
|
337
|
-
data:
|
337
|
+
data: GwEntityData,
|
338
338
|
measurements: dict[str, DATA | UOM],
|
339
339
|
) -> None:
|
340
340
|
"""Helper-function for _get_measurement_data() - collect appliance measurement data."""
|
@@ -359,21 +359,20 @@ class SmileLegacyHelper(SmileCommon):
|
|
359
359
|
appl_i_loc.text, ENERGY_WATT_HOUR
|
360
360
|
)
|
361
361
|
|
362
|
-
self.
|
363
|
-
self._count += len(data["sensors"])
|
364
|
-
self._count += len(data["switches"])
|
365
|
-
# Don't count the above top-level dicts, only the remaining single items
|
366
|
-
self._count += len(data) - 3
|
362
|
+
self._count_data_items(data)
|
367
363
|
|
368
364
|
def _get_actuator_functionalities(
|
369
|
-
self,
|
365
|
+
self,
|
366
|
+
xml: etree,
|
367
|
+
entity: GwEntityData,
|
368
|
+
data: GwEntityData
|
370
369
|
) -> None:
|
371
370
|
"""Helper-function for _get_measurement_data()."""
|
372
371
|
for item in ACTIVE_ACTUATORS:
|
373
372
|
# Skip max_dhw_temperature, not initially valid,
|
374
373
|
# skip thermostat for thermo_sensors
|
375
374
|
if item == "max_dhw_temperature" or (
|
376
|
-
item == "thermostat" and
|
375
|
+
item == "thermostat" and entity["dev_class"] == "thermo_sensor"
|
377
376
|
):
|
378
377
|
continue
|
379
378
|
|
@@ -401,7 +400,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
401
400
|
data[act_item] = temp_dict
|
402
401
|
|
403
402
|
def _object_value(self, obj_id: str, measurement: str) -> float | int | None:
|
404
|
-
"""Helper-function for smile.py:
|
403
|
+
"""Helper-function for smile.py: _get_entity_data().
|
405
404
|
|
406
405
|
Obtain the value/state for the given object from a location in DOMAIN_OBJECTS
|
407
406
|
"""
|
@@ -415,7 +414,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
415
414
|
return val
|
416
415
|
|
417
416
|
def _preset(self) -> str | None:
|
418
|
-
"""Helper-function for smile.py:
|
417
|
+
"""Helper-function for smile.py: _climate_data().
|
419
418
|
|
420
419
|
Collect the active preset based on the active rule.
|
421
420
|
"""
|
plugwise/legacy/smile.py
CHANGED
@@ -19,8 +19,8 @@ from plugwise.constants import (
|
|
19
19
|
OFF,
|
20
20
|
REQUIRE_APPLIANCES,
|
21
21
|
RULES,
|
22
|
-
DeviceData,
|
23
22
|
GatewayData,
|
23
|
+
GwEntityData,
|
24
24
|
PlugwiseData,
|
25
25
|
ThermoLoc,
|
26
26
|
)
|
@@ -44,11 +44,11 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
44
44
|
request: Callable[..., Awaitable[Any]],
|
45
45
|
websession: aiohttp.ClientSession,
|
46
46
|
_is_thermostat: bool,
|
47
|
+
_loc_data: dict[str, ThermoLoc],
|
47
48
|
_on_off_device: bool,
|
48
49
|
_opentherm_device: bool,
|
49
50
|
_stretch_v2: bool,
|
50
51
|
_target_smile: str,
|
51
|
-
loc_data: dict[str, ThermoLoc],
|
52
52
|
smile_fw_version: Version | None,
|
53
53
|
smile_hostname: str,
|
54
54
|
smile_hw_version: str | None,
|
@@ -61,15 +61,13 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
61
61
|
username: str = DEFAULT_USERNAME,
|
62
62
|
) -> None:
|
63
63
|
"""Set the constructor for this class."""
|
64
|
-
SmileLegacyData.__init__(self)
|
65
|
-
|
66
64
|
self._cooling_present = False
|
67
65
|
self._is_thermostat = _is_thermostat
|
66
|
+
self._loc_data = _loc_data
|
68
67
|
self._on_off_device = _on_off_device
|
69
68
|
self._opentherm_device = _opentherm_device
|
70
69
|
self._stretch_v2 = _stretch_v2
|
71
70
|
self._target_smile = _target_smile
|
72
|
-
self.loc_data = loc_data
|
73
71
|
self.request = request
|
74
72
|
self.smile_fw_version = smile_fw_version
|
75
73
|
self.smile_hostname = smile_hostname
|
@@ -79,10 +77,11 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
79
77
|
self.smile_name = smile_name
|
80
78
|
self.smile_type = smile_type
|
81
79
|
self.smile_zigbee_mac_address = smile_zigbee_mac_address
|
80
|
+
SmileLegacyData.__init__(self)
|
82
81
|
|
83
82
|
self._previous_day_number: str = "0"
|
84
83
|
|
85
|
-
async def
|
84
|
+
async def full_xml_update(self) -> None:
|
86
85
|
"""Perform a first fetch of all XML data, needed for initialization."""
|
87
86
|
self._domain_objects = await self.request(DOMAIN_OBJECTS)
|
88
87
|
self._locations = await self.request(LOCATIONS)
|
@@ -91,8 +90,8 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
91
90
|
if self.smile_type != "power":
|
92
91
|
self._appliances = await self.request(APPLIANCES)
|
93
92
|
|
94
|
-
def
|
95
|
-
"""
|
93
|
+
def get_all_gateway_entities(self) -> None:
|
94
|
+
"""Collect the gateway entities from the received raw XML-data.
|
96
95
|
|
97
96
|
Run this functions once to gather the initial device configuration,
|
98
97
|
then regularly run async_update() to refresh the device data.
|
@@ -102,10 +101,10 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
102
101
|
|
103
102
|
# Collect and add switching- and/or pump-group devices
|
104
103
|
if group_data := self._get_group_switches():
|
105
|
-
self.
|
104
|
+
self.gw_entities.update(group_data)
|
106
105
|
|
107
|
-
# Collect the remaining data for all
|
108
|
-
self.
|
106
|
+
# Collect the remaining data for all entities
|
107
|
+
self._all_entity_data()
|
109
108
|
|
110
109
|
async def async_update(self) -> PlugwiseData:
|
111
110
|
"""Perform an incremental update for updating the various device states."""
|
@@ -119,9 +118,9 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
119
118
|
"Performing daily full-update, reload the Plugwise integration when a single entity becomes unavailable."
|
120
119
|
)
|
121
120
|
self.gw_data: GatewayData = {}
|
122
|
-
self.
|
123
|
-
await self.
|
124
|
-
self.
|
121
|
+
self.gw_entities: dict[str, GwEntityData] = {}
|
122
|
+
await self.full_xml_update()
|
123
|
+
self.get_all_gateway_entities()
|
125
124
|
# Otherwise perform an incremental update
|
126
125
|
else:
|
127
126
|
self._domain_objects = await self.request(DOMAIN_OBJECTS)
|
@@ -131,10 +130,13 @@ class SmileLegacyAPI(SmileLegacyData):
|
|
131
130
|
case self._target_smile if self._target_smile in REQUIRE_APPLIANCES:
|
132
131
|
self._appliances = await self.request(APPLIANCES)
|
133
132
|
|
134
|
-
self.
|
133
|
+
self._update_gw_entities()
|
135
134
|
|
136
135
|
self._previous_day_number = day_number
|
137
|
-
return PlugwiseData(
|
136
|
+
return PlugwiseData(
|
137
|
+
devices=self.gw_entities,
|
138
|
+
gateway=self.gw_data,
|
139
|
+
)
|
138
140
|
|
139
141
|
########################################################################################################
|
140
142
|
### API Set and HA Service-related Functions ###
|
plugwise/smile.py
CHANGED
@@ -22,8 +22,8 @@ from plugwise.constants import (
|
|
22
22
|
NOTIFICATIONS,
|
23
23
|
OFF,
|
24
24
|
RULES,
|
25
|
-
DeviceData,
|
26
25
|
GatewayData,
|
26
|
+
GwEntityData,
|
27
27
|
PlugwiseData,
|
28
28
|
ThermoLoc,
|
29
29
|
)
|
@@ -53,11 +53,11 @@ class SmileAPI(SmileData):
|
|
53
53
|
_elga: bool,
|
54
54
|
_is_thermostat: bool,
|
55
55
|
_last_active: dict[str, str | None],
|
56
|
+
_loc_data: dict[str, ThermoLoc],
|
56
57
|
_on_off_device: bool,
|
57
58
|
_opentherm_device: bool,
|
58
59
|
_schedule_old_states: dict[str, dict[str, str]],
|
59
60
|
gateway_id: str,
|
60
|
-
loc_data: dict[str, ThermoLoc],
|
61
61
|
smile_fw_version: Version | None,
|
62
62
|
smile_hostname: str | None,
|
63
63
|
smile_hw_version: str | None,
|
@@ -70,17 +70,17 @@ class SmileAPI(SmileData):
|
|
70
70
|
username: str = DEFAULT_USERNAME,
|
71
71
|
) -> None:
|
72
72
|
"""Set the constructor for this class."""
|
73
|
-
|
74
|
-
|
73
|
+
self._cooling_enabled = False
|
75
74
|
self._cooling_present = _cooling_present
|
76
75
|
self._elga = _elga
|
76
|
+
self._heater_id: str
|
77
77
|
self._is_thermostat = _is_thermostat
|
78
78
|
self._last_active = _last_active
|
79
|
+
self._loc_data = _loc_data
|
79
80
|
self._on_off_device = _on_off_device
|
80
81
|
self._opentherm_device = _opentherm_device
|
81
82
|
self._schedule_old_states = _schedule_old_states
|
82
83
|
self.gateway_id = gateway_id
|
83
|
-
self.loc_data = loc_data
|
84
84
|
self.request = request
|
85
85
|
self.smile_fw_version = smile_fw_version
|
86
86
|
self.smile_hostname = smile_hostname
|
@@ -90,22 +90,21 @@ class SmileAPI(SmileData):
|
|
90
90
|
self.smile_model_id = smile_model_id
|
91
91
|
self.smile_name = smile_name
|
92
92
|
self.smile_type = smile_type
|
93
|
+
SmileData.__init__(self)
|
93
94
|
|
94
|
-
self._heater_id: str
|
95
|
-
self._cooling_enabled = False
|
96
95
|
|
97
|
-
async def
|
96
|
+
async def full_xml_update(self) -> None:
|
98
97
|
"""Perform a first fetch of all XML data, needed for initialization."""
|
99
98
|
self._domain_objects = await self.request(DOMAIN_OBJECTS)
|
100
99
|
self._get_plugwise_notifications()
|
101
100
|
|
102
|
-
def
|
103
|
-
"""
|
101
|
+
def get_all_gateway_entities(self) -> None:
|
102
|
+
"""Collect the gateway entities from the received raw XML-data.
|
104
103
|
|
105
|
-
Run this functions once to gather the initial
|
106
|
-
then regularly run async_update() to refresh the
|
104
|
+
Run this functions once to gather the initial configuration,
|
105
|
+
then regularly run async_update() to refresh the entity data.
|
107
106
|
"""
|
108
|
-
# Gather all the
|
107
|
+
# Gather all the entities and their initial data
|
109
108
|
self._all_appliances()
|
110
109
|
if self._is_thermostat:
|
111
110
|
if self.smile(ADAM):
|
@@ -117,20 +116,21 @@ class SmileAPI(SmileData):
|
|
117
116
|
|
118
117
|
# Collect and add switching- and/or pump-group devices
|
119
118
|
if group_data := self._get_group_switches():
|
120
|
-
self.
|
119
|
+
self.gw_entities.update(group_data)
|
121
120
|
|
122
|
-
# Collect the remaining data for all
|
123
|
-
self.
|
121
|
+
# Collect the remaining data for all entities
|
122
|
+
self._all_entity_data()
|
124
123
|
|
125
124
|
async def async_update(self) -> PlugwiseData:
|
126
125
|
"""Perform an incremental update for updating the various device states."""
|
127
126
|
self.gw_data: GatewayData = {}
|
128
|
-
self.
|
127
|
+
self.gw_entities: dict[str, GwEntityData] = {}
|
128
|
+
self._zones: dict[str, GwEntityData] = {}
|
129
129
|
try:
|
130
|
-
await self.
|
131
|
-
self.
|
130
|
+
await self.full_xml_update()
|
131
|
+
self.get_all_gateway_entities()
|
132
132
|
if "heater_id" in self.gw_data:
|
133
|
-
heat_cooler = self.
|
133
|
+
heat_cooler = self.gw_entities[self.gw_data["heater_id"]]
|
134
134
|
if (
|
135
135
|
"binary_sensors" in heat_cooler
|
136
136
|
and "cooling_enabled" in heat_cooler["binary_sensors"]
|
@@ -139,7 +139,10 @@ class SmileAPI(SmileData):
|
|
139
139
|
except KeyError as err:
|
140
140
|
raise DataMissingError("No Plugwise data received") from err
|
141
141
|
|
142
|
-
return PlugwiseData(
|
142
|
+
return PlugwiseData(
|
143
|
+
devices=self.gw_entities,
|
144
|
+
gateway=self.gw_data,
|
145
|
+
)
|
143
146
|
|
144
147
|
########################################################################################################
|
145
148
|
### API Set and HA Service-related Functions ###
|
plugwise/util.py
CHANGED
@@ -22,8 +22,8 @@ from plugwise.constants import (
|
|
22
22
|
TEMP_CELSIUS,
|
23
23
|
UOM,
|
24
24
|
BinarySensorType,
|
25
|
-
|
26
|
-
|
25
|
+
GwEntityData,
|
26
|
+
ModuleData,
|
27
27
|
SensorType,
|
28
28
|
SpecialType,
|
29
29
|
SwitchType,
|
@@ -115,14 +115,14 @@ def check_model(name: str | None, vendor_name: str | None) -> str | None:
|
|
115
115
|
if name is not None and "lumi.plug" in name:
|
116
116
|
return "Aqara Smart Plug"
|
117
117
|
|
118
|
-
return
|
118
|
+
return None
|
119
119
|
|
120
120
|
|
121
121
|
def common_match_cases(
|
122
122
|
measurement: str,
|
123
123
|
attrs: DATA | UOM,
|
124
124
|
location: etree,
|
125
|
-
data:
|
125
|
+
data: GwEntityData,
|
126
126
|
) -> None:
|
127
127
|
"""Helper-function for common match-case execution."""
|
128
128
|
value = location.text in ("on", "true")
|
@@ -173,15 +173,13 @@ def format_measure(measure: str, unit: str) -> float | int:
|
|
173
173
|
result = float(f"{round(float_measure, 1):.1f}")
|
174
174
|
elif abs(float_measure) < 10:
|
175
175
|
result = float(f"{round(float_measure, 2):.2f}")
|
176
|
-
elif abs(float_measure) >= 10
|
176
|
+
elif abs(float_measure) >= 10:
|
177
177
|
result = float(f"{round(float_measure, 1):.1f}")
|
178
|
-
elif abs(float_measure) >= 100:
|
179
|
-
result = int(round(float_measure))
|
180
178
|
|
181
179
|
return result
|
182
180
|
|
183
181
|
|
184
|
-
def get_vendor_name(module: etree, model_data:
|
182
|
+
def get_vendor_name(module: etree, model_data: ModuleData) -> ModuleData:
|
185
183
|
"""Helper-function for _get_model_data()."""
|
186
184
|
if (vendor_name := module.find("vendor_name").text) is not None:
|
187
185
|
model_data["vendor_name"] = vendor_name
|
@@ -204,7 +202,7 @@ def power_data_local_format(
|
|
204
202
|
return format_measure(val, attrs_uom)
|
205
203
|
|
206
204
|
|
207
|
-
def remove_empty_platform_dicts(data:
|
205
|
+
def remove_empty_platform_dicts(data: GwEntityData) -> None:
|
208
206
|
"""Helper-function for removing any empty platform dicts."""
|
209
207
|
if not data["binary_sensors"]:
|
210
208
|
data.pop("binary_sensors")
|