plugwise 1.5.2__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 +60 -50
- plugwise/constants.py +21 -9
- plugwise/data.py +95 -67
- plugwise/helper.py +193 -137
- plugwise/legacy/data.py +23 -23
- plugwise/legacy/helper.py +54 -55
- plugwise/legacy/smile.py +18 -16
- plugwise/smile.py +24 -21
- plugwise/util.py +5 -5
- {plugwise-1.5.2.dist-info → plugwise-1.6.0.dist-info}/METADATA +1 -1
- plugwise-1.6.0.dist-info/RECORD +17 -0
- {plugwise-1.5.2.dist-info → plugwise-1.6.0.dist-info}/WHEEL +1 -1
- plugwise-1.5.2.dist-info/RECORD +0 -17
- {plugwise-1.5.2.dist-info → plugwise-1.6.0.dist-info}/LICENSE +0 -0
- {plugwise-1.5.2.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 (
|
@@ -139,20 +139,20 @@ class SmileLegacyHelper(SmileCommon):
|
|
139
139
|
continue
|
140
140
|
|
141
141
|
# Skip orphaned heater_central (Core Issue #104433)
|
142
|
-
if appl.pwclass == "heater_central" and appl.
|
142
|
+
if appl.pwclass == "heater_central" and appl.entity_id != self._heater_id:
|
143
143
|
continue # pragma: no cover
|
144
144
|
|
145
|
-
self.
|
145
|
+
self._create_gw_entities(appl)
|
146
146
|
|
147
147
|
# Place the gateway and optional heater_central devices as 1st and 2nd
|
148
148
|
for dev_class in ("heater_central", "gateway"):
|
149
|
-
for
|
150
|
-
if
|
151
|
-
|
152
|
-
self.
|
153
|
-
cleared_dict = self.
|
154
|
-
add_to_front = {
|
155
|
-
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}
|
156
156
|
|
157
157
|
def _all_locations(self) -> None:
|
158
158
|
"""Collect all locations."""
|
@@ -161,7 +161,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
161
161
|
# Legacy Anna without outdoor_temp and Stretches have no locations, create fake location-data
|
162
162
|
if not (locations := self._locations.findall("./location")):
|
163
163
|
self._home_location = FAKE_LOC
|
164
|
-
self.
|
164
|
+
self._loc_data[FAKE_LOC] = {"name": "Home"}
|
165
165
|
return
|
166
166
|
|
167
167
|
for location in locations:
|
@@ -182,18 +182,18 @@ class SmileLegacyHelper(SmileCommon):
|
|
182
182
|
loc.name = "Home"
|
183
183
|
self._home_location = loc.loc_id
|
184
184
|
|
185
|
-
self.
|
185
|
+
self._loc_data[loc.loc_id] = {"name": loc.name}
|
186
186
|
|
187
187
|
def _create_legacy_gateway(self) -> None:
|
188
|
-
"""Create the (missing) gateway
|
188
|
+
"""Create the (missing) gateway entities for legacy Anna, P1 and Stretch.
|
189
189
|
|
190
|
-
Use the home_location or FAKE_APPL as
|
190
|
+
Use the home_location or FAKE_APPL as entity id.
|
191
191
|
"""
|
192
192
|
self.gateway_id = self._home_location
|
193
193
|
if self.smile_type == "power":
|
194
194
|
self.gateway_id = FAKE_APPL
|
195
195
|
|
196
|
-
self.
|
196
|
+
self.gw_entities[self.gateway_id] = {"dev_class": "gateway"}
|
197
197
|
self._count += 1
|
198
198
|
for key, value in {
|
199
199
|
"firmware": str(self.smile_fw_version),
|
@@ -206,28 +206,28 @@ class SmileLegacyHelper(SmileCommon):
|
|
206
206
|
}.items():
|
207
207
|
if value is not None:
|
208
208
|
gw_key = cast(ApplianceType, key)
|
209
|
-
self.
|
209
|
+
self.gw_entities[self.gateway_id][gw_key] = value
|
210
210
|
self._count += 1
|
211
211
|
|
212
212
|
def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
|
213
|
-
"""Collect
|
213
|
+
"""Collect entity info (Smile/Stretch, Thermostats, OpenTherm/On-Off): firmware, model and vendor name."""
|
214
214
|
match appl.pwclass:
|
215
|
-
# Collect thermostat
|
215
|
+
# Collect thermostat entity info
|
216
216
|
case _ as dev_class if dev_class in THERMOSTAT_CLASSES:
|
217
217
|
return self._appl_thermostat_info(appl, appliance, self._modules)
|
218
|
-
# Collect heater_central
|
218
|
+
# Collect heater_central entity info
|
219
219
|
case "heater_central":
|
220
220
|
return self._appl_heater_central_info(
|
221
221
|
appl, appliance, True, self._appliances, self._modules
|
222
222
|
) # True means legacy device
|
223
223
|
# Collect info from Stretches
|
224
224
|
case _:
|
225
|
-
return self.
|
225
|
+
return self._energy_entity_info_finder(appliance, appl)
|
226
226
|
|
227
|
-
def
|
227
|
+
def _energy_entity_info_finder(self, appliance: etree, appl: Munch) -> Munch:
|
228
228
|
"""Helper-function for _appliance_info_finder().
|
229
229
|
|
230
|
-
Collect energy
|
230
|
+
Collect energy entity info (Smartmeter, Circle, Stealth, etc.): firmware, model and vendor name.
|
231
231
|
"""
|
232
232
|
if self.smile_type in ("power", "stretch"):
|
233
233
|
locator = "./services/electricity_point_meter"
|
@@ -251,9 +251,9 @@ class SmileLegacyHelper(SmileCommon):
|
|
251
251
|
|
252
252
|
def _p1_smartmeter_info_finder(self, appl: Munch) -> None:
|
253
253
|
"""Collect P1 DSMR Smartmeter info."""
|
254
|
-
loc_id = next(iter(self.
|
254
|
+
loc_id = next(iter(self._loc_data.keys()))
|
255
255
|
appl.available = None
|
256
|
-
appl.
|
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,
|
@@ -122,7 +122,7 @@ 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")
|
@@ -179,7 +179,7 @@ def format_measure(measure: str, unit: str) -> float | int:
|
|
179
179
|
return result
|
180
180
|
|
181
181
|
|
182
|
-
def get_vendor_name(module: etree, model_data:
|
182
|
+
def get_vendor_name(module: etree, model_data: ModuleData) -> ModuleData:
|
183
183
|
"""Helper-function for _get_model_data()."""
|
184
184
|
if (vendor_name := module.find("vendor_name").text) is not None:
|
185
185
|
model_data["vendor_name"] = vendor_name
|
@@ -202,7 +202,7 @@ def power_data_local_format(
|
|
202
202
|
return format_measure(val, attrs_uom)
|
203
203
|
|
204
204
|
|
205
|
-
def remove_empty_platform_dicts(data:
|
205
|
+
def remove_empty_platform_dicts(data: GwEntityData) -> None:
|
206
206
|
"""Helper-function for removing any empty platform dicts."""
|
207
207
|
if not data["binary_sensors"]:
|
208
208
|
data.pop("binary_sensors")
|
@@ -0,0 +1,17 @@
|
|
1
|
+
plugwise/__init__.py,sha256=iMNBAFzwPGoCue0i3NrWekZKHFMJ5gzeStNGSR_L8F8,17151
|
2
|
+
plugwise/common.py,sha256=vyiAbn5SJgcL5A9DYIj2ixHBbPO_6EFa16bK1VJ3In4,13040
|
3
|
+
plugwise/constants.py,sha256=vVn407-iivZIZ1ZqwHsnWJ4KjK3PSFRFUhOfDl9Qn6E,17129
|
4
|
+
plugwise/data.py,sha256=7av0Ditk_dW9L7Ojoj7xo4-pI4FHkh1KhkBsl2nmP9Q,11537
|
5
|
+
plugwise/exceptions.py,sha256=Ce-tO9uNsMB-8FP6VAxBvsHNJ-NIM9F0onUZOdZI4Ys,1110
|
6
|
+
plugwise/helper.py,sha256=R85dRvIgjjLx2q02iQXdW34HFl4ibS1-sJfadijBnxI,44604
|
7
|
+
plugwise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
plugwise/smile.py,sha256=tDpoAkkdY8OATmCqAfR5M-rtEvV_L3iic_929dXJwOI,18851
|
9
|
+
plugwise/util.py,sha256=kt7JNcrTQaLT3eW_fzO4YmGsZzRull2Ge_OmHKl-rHk,8054
|
10
|
+
plugwise/legacy/data.py,sha256=wHNcRQ_qF4A1rUdxn-1MoW1Z1gUwLqOvYvIkN6tJ_sk,3088
|
11
|
+
plugwise/legacy/helper.py,sha256=ARIJytJNFiIR5G7Bp75DIULqgt56m0pxUXy6Ze8Te-4,18173
|
12
|
+
plugwise/legacy/smile.py,sha256=RCQ0kHQwmPjq_G3r6aCe75RIvJt339jilzqEKydNopo,11286
|
13
|
+
plugwise-1.6.0.dist-info/LICENSE,sha256=mL22BjmXtg_wnoDnnaqps5_Bg_VGj_yHueX5lsKwbCc,1144
|
14
|
+
plugwise-1.6.0.dist-info/METADATA,sha256=heMjNq5fZujx2FHjceBPZcUdo_ZLG5rr6YvbReZd8VU,9097
|
15
|
+
plugwise-1.6.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
16
|
+
plugwise-1.6.0.dist-info/top_level.txt,sha256=MYOmktMFf8ZmX6_OE1y9MoCZFfY-L8DA0F2tA2IvE4s,9
|
17
|
+
plugwise-1.6.0.dist-info/RECORD,,
|