plugwise 1.5.2__tar.gz → 1.6.0__tar.gz
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-1.5.2 → plugwise-1.6.0}/PKG-INFO +1 -1
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/__init__.py +10 -10
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/common.py +60 -50
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/constants.py +21 -9
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/data.py +95 -67
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/helper.py +193 -137
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/legacy/data.py +23 -23
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/legacy/helper.py +54 -55
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/legacy/smile.py +18 -16
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/smile.py +24 -21
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/util.py +5 -5
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise.egg-info/PKG-INFO +1 -1
- {plugwise-1.5.2 → plugwise-1.6.0}/pyproject.toml +3 -2
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_adam.py +10 -8
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_anna.py +17 -21
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_init.py +70 -61
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_legacy_anna.py +2 -2
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_legacy_p1.py +2 -3
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_legacy_stretch.py +3 -3
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_p1.py +2 -26
- {plugwise-1.5.2 → plugwise-1.6.0}/LICENSE +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/README.md +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/exceptions.py +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise/py.typed +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise.egg-info/SOURCES.txt +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise.egg-info/dependency_links.txt +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise.egg-info/requires.txt +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/plugwise.egg-info/top_level.txt +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/setup.cfg +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/setup.py +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_generic.py +0 -0
- {plugwise-1.5.2 → plugwise-1.6.0}/tests/test_legacy_generic.py +0 -0
@@ -69,6 +69,7 @@ class Smile(SmileComm):
|
|
69
69
|
self._elga = False
|
70
70
|
self._is_thermostat = False
|
71
71
|
self._last_active: dict[str, str | None] = {}
|
72
|
+
self._loc_data: dict[str, ThermoLoc] = {}
|
72
73
|
self._on_off_device = False
|
73
74
|
self._opentherm_device = False
|
74
75
|
self._schedule_old_states: dict[str, dict[str, str]] = {}
|
@@ -76,7 +77,6 @@ class Smile(SmileComm):
|
|
76
77
|
self._stretch_v2 = False
|
77
78
|
self._target_smile: str = NONE
|
78
79
|
self.gateway_id: str = NONE
|
79
|
-
self.loc_data: dict[str, ThermoLoc] = {}
|
80
80
|
self.smile_fw_version: Version | None = None
|
81
81
|
self.smile_hostname: str = NONE
|
82
82
|
self.smile_hw_version: str | None = None
|
@@ -134,11 +134,11 @@ class Smile(SmileComm):
|
|
134
134
|
self._elga,
|
135
135
|
self._is_thermostat,
|
136
136
|
self._last_active,
|
137
|
+
self._loc_data,
|
137
138
|
self._on_off_device,
|
138
139
|
self._opentherm_device,
|
139
140
|
self._schedule_old_states,
|
140
141
|
self.gateway_id,
|
141
|
-
self.loc_data,
|
142
142
|
self.smile_fw_version,
|
143
143
|
self.smile_hostname,
|
144
144
|
self.smile_hw_version,
|
@@ -155,11 +155,11 @@ class Smile(SmileComm):
|
|
155
155
|
self._request,
|
156
156
|
self._websession,
|
157
157
|
self._is_thermostat,
|
158
|
+
self._loc_data,
|
158
159
|
self._on_off_device,
|
159
160
|
self._opentherm_device,
|
160
161
|
self._stretch_v2,
|
161
162
|
self._target_smile,
|
162
|
-
self.loc_data,
|
163
163
|
self.smile_fw_version,
|
164
164
|
self.smile_hostname,
|
165
165
|
self.smile_hw_version,
|
@@ -173,7 +173,7 @@ class Smile(SmileComm):
|
|
173
173
|
)
|
174
174
|
|
175
175
|
# Update all endpoints on first connect
|
176
|
-
await self._smile_api.
|
176
|
+
await self._smile_api.full_xml_update()
|
177
177
|
|
178
178
|
return self.smile_version
|
179
179
|
|
@@ -298,17 +298,17 @@ class Smile(SmileComm):
|
|
298
298
|
self.smile_legacy = True
|
299
299
|
return return_model
|
300
300
|
|
301
|
-
async def
|
301
|
+
async def full_xml_update(self) -> None:
|
302
302
|
"""Helper-function used for testing."""
|
303
|
-
await self._smile_api.
|
303
|
+
await self._smile_api.full_xml_update()
|
304
304
|
|
305
|
-
def
|
305
|
+
def get_all_gateway_entities(self) -> None:
|
306
306
|
"""Helper-function used for testing."""
|
307
|
-
self._smile_api.
|
307
|
+
self._smile_api.get_all_gateway_entities()
|
308
308
|
|
309
309
|
async def async_update(self) -> PlugwiseData:
|
310
|
-
"""
|
311
|
-
data = PlugwiseData({}, {})
|
310
|
+
"""Update the various entities and their states."""
|
311
|
+
data = PlugwiseData(devices={}, gateway={})
|
312
312
|
try:
|
313
313
|
data = await self._smile_api.async_update()
|
314
314
|
self.gateway_id = data.gateway["gateway_id"]
|
@@ -11,8 +11,8 @@ from plugwise.constants import (
|
|
11
11
|
SPECIAL_PLUG_TYPES,
|
12
12
|
SWITCH_GROUP_TYPES,
|
13
13
|
ApplianceType,
|
14
|
-
|
15
|
-
|
14
|
+
GwEntityData,
|
15
|
+
ModuleData,
|
16
16
|
SensorType,
|
17
17
|
)
|
18
18
|
from plugwise.util import (
|
@@ -40,7 +40,7 @@ class SmileCommon:
|
|
40
40
|
self._heater_id: str
|
41
41
|
self._on_off_device: bool
|
42
42
|
self._opentherm_device: bool
|
43
|
-
self.
|
43
|
+
self.gw_entities: dict[str, GwEntityData]
|
44
44
|
self.smile_name: str
|
45
45
|
self.smile_type: str
|
46
46
|
|
@@ -108,7 +108,7 @@ class SmileCommon:
|
|
108
108
|
|
109
109
|
return appl
|
110
110
|
|
111
|
-
def _collect_power_values(self, data:
|
111
|
+
def _collect_power_values(self, data: GwEntityData, loc: Munch, tariff: str, legacy: bool = False) -> None:
|
112
112
|
"""Something."""
|
113
113
|
for loc.peak_select in ("nl_peak", "nl_offpeak"):
|
114
114
|
loc.locator = (
|
@@ -131,6 +131,19 @@ class SmileCommon:
|
|
131
131
|
key = cast(SensorType, loc.key_string)
|
132
132
|
data["sensors"][key] = loc.f_val
|
133
133
|
|
134
|
+
def _count_data_items(self, data: GwEntityData) -> None:
|
135
|
+
"""When present, count the binary_sensors, sensors and switches dict-items, don't count the dicts.
|
136
|
+
|
137
|
+
Also, count the remaining single data items, the amount of dicts present have already been pre-subtracted in the previous step.
|
138
|
+
"""
|
139
|
+
if "binary_sensors" in data:
|
140
|
+
self._count += len(data["binary_sensors"]) - 1
|
141
|
+
if "sensors" in data:
|
142
|
+
self._count += len(data["sensors"]) - 1
|
143
|
+
if "switches" in data:
|
144
|
+
self._count += len(data["switches"]) - 1
|
145
|
+
self._count += len(data)
|
146
|
+
|
134
147
|
def _power_data_peak_value(self, loc: Munch, legacy: bool) -> Munch:
|
135
148
|
"""Helper-function for _power_data_from_location() and _power_data_from_modules()."""
|
136
149
|
loc.found = True
|
@@ -160,8 +173,8 @@ class SmileCommon:
|
|
160
173
|
measurement: str,
|
161
174
|
net_string: SensorType,
|
162
175
|
f_val: float | int,
|
163
|
-
|
164
|
-
) ->
|
176
|
+
data: GwEntityData,
|
177
|
+
) -> GwEntityData:
|
165
178
|
"""Calculate differential energy."""
|
166
179
|
if (
|
167
180
|
"electricity" in measurement
|
@@ -171,10 +184,10 @@ class SmileCommon:
|
|
171
184
|
diff = 1
|
172
185
|
if "produced" in measurement:
|
173
186
|
diff = -1
|
174
|
-
if net_string not in
|
187
|
+
if net_string not in data["sensors"]:
|
175
188
|
tmp_val: float | int = 0
|
176
189
|
else:
|
177
|
-
tmp_val =
|
190
|
+
tmp_val = data["sensors"][net_string]
|
178
191
|
|
179
192
|
if isinstance(f_val, int):
|
180
193
|
tmp_val += f_val * diff
|
@@ -182,13 +195,13 @@ class SmileCommon:
|
|
182
195
|
tmp_val += float(f_val * diff)
|
183
196
|
tmp_val = float(f"{round(tmp_val, 3):.3f}")
|
184
197
|
|
185
|
-
|
198
|
+
data["sensors"][net_string] = tmp_val
|
186
199
|
|
187
|
-
return
|
200
|
+
return data
|
188
201
|
|
189
|
-
def
|
190
|
-
"""Helper-function for creating/updating
|
191
|
-
self.
|
202
|
+
def _create_gw_entities(self, appl: Munch) -> None:
|
203
|
+
"""Helper-function for creating/updating gw_entities."""
|
204
|
+
self.gw_entities[appl.entity_id] = {"dev_class": appl.pwclass}
|
192
205
|
self._count += 1
|
193
206
|
for key, value in {
|
194
207
|
"available": appl.available,
|
@@ -204,30 +217,30 @@ class SmileCommon:
|
|
204
217
|
}.items():
|
205
218
|
if value is not None or key == "location":
|
206
219
|
appl_key = cast(ApplianceType, key)
|
207
|
-
self.
|
220
|
+
self.gw_entities[appl.entity_id][appl_key] = value
|
208
221
|
self._count += 1
|
209
222
|
|
210
|
-
def
|
211
|
-
self,
|
223
|
+
def _entity_switching_group(
|
224
|
+
self, entity: GwEntityData, data: GwEntityData
|
212
225
|
) -> None:
|
213
|
-
"""Helper-function for
|
226
|
+
"""Helper-function for _get_device_zone_data().
|
214
227
|
|
215
228
|
Determine switching group device data.
|
216
229
|
"""
|
217
|
-
if
|
230
|
+
if entity["dev_class"] in SWITCH_GROUP_TYPES:
|
218
231
|
counter = 0
|
219
|
-
for member in
|
220
|
-
if self.
|
232
|
+
for member in entity["members"]:
|
233
|
+
if self.gw_entities[member]["switches"].get("relay"):
|
221
234
|
counter += 1
|
222
235
|
data["switches"]["relay"] = counter != 0
|
223
236
|
self._count += 1
|
224
237
|
|
225
|
-
def _get_group_switches(self) -> dict[str,
|
226
|
-
"""Helper-function for smile.py:
|
238
|
+
def _get_group_switches(self) -> dict[str, GwEntityData]:
|
239
|
+
"""Helper-function for smile.py: get_all_gateway_entities().
|
227
240
|
|
228
241
|
Collect switching- or pump-group info.
|
229
242
|
"""
|
230
|
-
switch_groups: dict[str,
|
243
|
+
switch_groups: dict[str, GwEntityData] = {}
|
231
244
|
# P1 and Anna don't have switchgroups
|
232
245
|
if self.smile_type == "power" or self.smile(ANNA):
|
233
246
|
return switch_groups
|
@@ -240,25 +253,22 @@ class SmileCommon:
|
|
240
253
|
group_appliances = group.findall("appliances/appliance")
|
241
254
|
for item in group_appliances:
|
242
255
|
# Check if members are not orphaned - stretch
|
243
|
-
if item.attrib["id"] in self.
|
256
|
+
if item.attrib["id"] in self.gw_entities:
|
244
257
|
members.append(item.attrib["id"])
|
245
258
|
|
246
259
|
if group_type in SWITCH_GROUP_TYPES and members:
|
247
|
-
switch_groups
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
},
|
255
|
-
},
|
256
|
-
)
|
260
|
+
switch_groups[group_id] = {
|
261
|
+
"dev_class": group_type,
|
262
|
+
"model": "Switchgroup",
|
263
|
+
"name": group_name,
|
264
|
+
"members": members,
|
265
|
+
"vendor": "Plugwise",
|
266
|
+
}
|
257
267
|
self._count += 4
|
258
268
|
|
259
269
|
return switch_groups
|
260
270
|
|
261
|
-
def _get_lock_state(self, xml: etree, data:
|
271
|
+
def _get_lock_state(self, xml: etree, data: GwEntityData, stretch_v2: bool = False) -> None:
|
262
272
|
"""Helper-function for _get_measurement_data().
|
263
273
|
|
264
274
|
Adam & Stretches: obtain the relay-switch lock state.
|
@@ -280,12 +290,12 @@ class SmileCommon:
|
|
280
290
|
locator: str,
|
281
291
|
xml_2: etree = None,
|
282
292
|
legacy: bool = False,
|
283
|
-
) ->
|
293
|
+
) -> ModuleData:
|
284
294
|
"""Helper-function for _energy_device_info_finder() and _appliance_info_finder().
|
285
295
|
|
286
296
|
Collect requested info from MODULES.
|
287
297
|
"""
|
288
|
-
|
298
|
+
module_data: ModuleData = {
|
289
299
|
"contents": False,
|
290
300
|
"firmware_version": None,
|
291
301
|
"hardware_version": None,
|
@@ -304,25 +314,25 @@ class SmileCommon:
|
|
304
314
|
search = return_valid(xml_2, self._domain_objects)
|
305
315
|
module = search.find(loc)
|
306
316
|
if module is not None: # pylint: disable=consider-using-assignment-expr
|
307
|
-
|
308
|
-
get_vendor_name(module,
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
self._get_zigbee_data(module,
|
317
|
+
module_data["contents"] = True
|
318
|
+
get_vendor_name(module, module_data)
|
319
|
+
module_data["vendor_model"] = module.find("vendor_model").text
|
320
|
+
module_data["hardware_version"] = module.find("hardware_version").text
|
321
|
+
module_data["firmware_version"] = module.find("firmware_version").text
|
322
|
+
self._get_zigbee_data(module, module_data, legacy)
|
313
323
|
|
314
|
-
return
|
324
|
+
return module_data
|
315
325
|
|
316
|
-
def _get_zigbee_data(self, module: etree,
|
317
|
-
"""Helper-function for
|
326
|
+
def _get_zigbee_data(self, module: etree, module_data: ModuleData, legacy: bool) -> None:
|
327
|
+
"""Helper-function for _get_module_data()."""
|
318
328
|
if legacy:
|
319
329
|
# Stretches
|
320
330
|
if (router := module.find("./protocols/network_router")) is not None:
|
321
|
-
|
331
|
+
module_data["zigbee_mac_address"] = router.find("mac_address").text
|
322
332
|
# Also look for the Circle+/Stealth M+
|
323
333
|
if (coord := module.find("./protocols/network_coordinator")) is not None:
|
324
|
-
|
334
|
+
module_data["zigbee_mac_address"] = coord.find("mac_address").text
|
325
335
|
# Adam
|
326
336
|
elif (zb_node := module.find("./protocols/zig_bee_node")) is not None:
|
327
|
-
|
328
|
-
|
337
|
+
module_data["zigbee_mac_address"] = zb_node.find("mac_address").text
|
338
|
+
module_data["reachable"] = zb_node.find("reachable").text == "true"
|
@@ -190,6 +190,14 @@ OBSOLETE_MEASUREMENTS: Final[tuple[str, ...]] = (
|
|
190
190
|
"outdoor_temperature",
|
191
191
|
)
|
192
192
|
|
193
|
+
# Zone/climate related measurements
|
194
|
+
ZONE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
|
195
|
+
"electricity_consumed": UOM(POWER_WATT),
|
196
|
+
"electricity_produced": UOM(POWER_WATT),
|
197
|
+
"relay": UOM(NONE),
|
198
|
+
"temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature
|
199
|
+
}
|
200
|
+
|
193
201
|
# Literals
|
194
202
|
SMILE_P1 = "Smile P1"
|
195
203
|
POWER = "power"
|
@@ -397,8 +405,8 @@ class GatewayData(TypedDict, total=False):
|
|
397
405
|
smile_name: str
|
398
406
|
|
399
407
|
|
400
|
-
class
|
401
|
-
"""The
|
408
|
+
class ModuleData(TypedDict):
|
409
|
+
"""The Module data class."""
|
402
410
|
|
403
411
|
contents: bool
|
404
412
|
firmware_version: str | None
|
@@ -492,9 +500,9 @@ class ThermoLoc(TypedDict, total=False):
|
|
492
500
|
"""Thermo Location class."""
|
493
501
|
|
494
502
|
name: str
|
495
|
-
primary: str
|
503
|
+
primary: list[str]
|
496
504
|
primary_prio: int
|
497
|
-
secondary:
|
505
|
+
secondary: list[str]
|
498
506
|
|
499
507
|
|
500
508
|
class ActuatorData(TypedDict, total=False):
|
@@ -508,8 +516,11 @@ class ActuatorData(TypedDict, total=False):
|
|
508
516
|
upper_bound: float
|
509
517
|
|
510
518
|
|
511
|
-
class
|
512
|
-
"""The
|
519
|
+
class GwEntityData(TypedDict, total=False):
|
520
|
+
"""The Gateway Entity data class.
|
521
|
+
|
522
|
+
Covering the collected output-data per device or location.
|
523
|
+
"""
|
513
524
|
|
514
525
|
# Appliance base data
|
515
526
|
dev_class: str
|
@@ -544,13 +555,13 @@ class DeviceData(TypedDict, total=False):
|
|
544
555
|
select_gateway_mode: str
|
545
556
|
select_regulation_mode: str
|
546
557
|
|
547
|
-
#
|
558
|
+
# Thermostat-related
|
559
|
+
thermostats: dict[str, list[str]]
|
548
560
|
# Presets:
|
549
561
|
active_preset: str | None
|
550
562
|
preset_modes: list[str] | None
|
551
563
|
# Schedules:
|
552
564
|
available_schedules: list[str]
|
553
|
-
last_used: str | None
|
554
565
|
select_schedule: str
|
555
566
|
|
556
567
|
climate_mode: str
|
@@ -571,5 +582,6 @@ class DeviceData(TypedDict, total=False):
|
|
571
582
|
class PlugwiseData:
|
572
583
|
"""Plugwise data provided as output."""
|
573
584
|
|
585
|
+
devices: dict[str, GwEntityData]
|
574
586
|
gateway: GatewayData
|
575
|
-
|
587
|
+
|
@@ -13,9 +13,8 @@ from plugwise.constants import (
|
|
13
13
|
MIN_SETPOINT,
|
14
14
|
NONE,
|
15
15
|
OFF,
|
16
|
-
ZONE_THERMOSTATS,
|
17
16
|
ActuatorData,
|
18
|
-
|
17
|
+
GwEntityData,
|
19
18
|
)
|
20
19
|
from plugwise.helper import SmileHelper
|
21
20
|
from plugwise.util import remove_empty_platform_dicts
|
@@ -29,12 +28,16 @@ class SmileData(SmileHelper):
|
|
29
28
|
SmileHelper.__init__(self)
|
30
29
|
|
31
30
|
|
32
|
-
def
|
33
|
-
"""Helper-function for
|
31
|
+
def _all_entity_data(self) -> None:
|
32
|
+
"""Helper-function for get_all_gateway_entities().
|
34
33
|
|
35
|
-
Collect data for each
|
34
|
+
Collect data for each entity and add to self.gw_data and self.gw_entities.
|
36
35
|
"""
|
37
|
-
self.
|
36
|
+
self._update_gw_entities()
|
37
|
+
if self.smile(ADAM):
|
38
|
+
self._update_zones()
|
39
|
+
self.gw_entities.update(self._zones)
|
40
|
+
|
38
41
|
self.gw_data.update(
|
39
42
|
{
|
40
43
|
"gateway_id": self.gateway_id,
|
@@ -49,31 +52,40 @@ class SmileData(SmileHelper):
|
|
49
52
|
{"heater_id": self._heater_id, "cooling_present": self._cooling_present}
|
50
53
|
)
|
51
54
|
|
52
|
-
def
|
53
|
-
"""Helper-function for
|
55
|
+
def _update_zones(self) -> None:
|
56
|
+
"""Helper-function for _all_entity_data() and async_update().
|
57
|
+
|
58
|
+
Collect data for each zone/location and add to self._zones.
|
59
|
+
"""
|
60
|
+
for location_id, zone in self._zones.items():
|
61
|
+
data = self._get_location_data(location_id)
|
62
|
+
zone.update(data)
|
63
|
+
|
64
|
+
def _update_gw_entities(self) -> None:
|
65
|
+
"""Helper-function for _all_entities_data() and async_update().
|
54
66
|
|
55
|
-
Collect data for each
|
67
|
+
Collect data for each entity and add to self.gw_entities.
|
56
68
|
"""
|
57
69
|
mac_list: list[str] = []
|
58
|
-
for
|
59
|
-
data = self.
|
60
|
-
if
|
70
|
+
for entity_id, entity in self.gw_entities.items():
|
71
|
+
data = self._get_entity_data(entity_id)
|
72
|
+
if entity_id == self.gateway_id:
|
61
73
|
mac_list = self._detect_low_batteries()
|
62
|
-
self._add_or_update_notifications(
|
74
|
+
self._add_or_update_notifications(entity_id, entity, data)
|
63
75
|
|
64
|
-
|
76
|
+
entity.update(data)
|
65
77
|
is_battery_low = (
|
66
78
|
mac_list
|
67
|
-
and "low_battery" in
|
68
|
-
and
|
69
|
-
and
|
79
|
+
and "low_battery" in entity["binary_sensors"]
|
80
|
+
and entity["zigbee_mac_address"] in mac_list
|
81
|
+
and entity["dev_class"] in ("thermo_sensor", "thermostatic_radiator_valve", "zone_thermometer", "zone_thermostat")
|
70
82
|
)
|
71
83
|
if is_battery_low:
|
72
|
-
|
84
|
+
entity["binary_sensors"]["low_battery"] = True
|
73
85
|
|
74
|
-
self._update_for_cooling(
|
86
|
+
self._update_for_cooling(entity)
|
75
87
|
|
76
|
-
remove_empty_platform_dicts(
|
88
|
+
remove_empty_platform_dicts(entity)
|
77
89
|
|
78
90
|
def _detect_low_batteries(self) -> list[str]:
|
79
91
|
"""Helper-function updating the low-battery binary_sensor status from a Battery-is-low message."""
|
@@ -97,31 +109,31 @@ class SmileData(SmileHelper):
|
|
97
109
|
return mac_address_list
|
98
110
|
|
99
111
|
def _add_or_update_notifications(
|
100
|
-
self,
|
112
|
+
self, entity_id: str, entity: GwEntityData, data: GwEntityData
|
101
113
|
) -> None:
|
102
114
|
"""Helper-function adding or updating the Plugwise notifications."""
|
103
115
|
if (
|
104
|
-
|
116
|
+
entity_id == self.gateway_id
|
105
117
|
and (
|
106
118
|
self._is_thermostat or self.smile_type == "power"
|
107
119
|
)
|
108
120
|
) or (
|
109
|
-
"binary_sensors" in
|
110
|
-
and "plugwise_notification" in
|
121
|
+
"binary_sensors" in entity
|
122
|
+
and "plugwise_notification" in entity["binary_sensors"]
|
111
123
|
):
|
112
124
|
data["binary_sensors"]["plugwise_notification"] = bool(self._notifications)
|
113
125
|
self._count += 1
|
114
126
|
|
115
|
-
def _update_for_cooling(self,
|
127
|
+
def _update_for_cooling(self, entity: GwEntityData) -> None:
|
116
128
|
"""Helper-function for adding/updating various cooling-related values."""
|
117
129
|
# For Anna and heating + cooling, replace setpoint with setpoint_high/_low
|
118
130
|
if (
|
119
131
|
self.smile(ANNA)
|
120
132
|
and self._cooling_present
|
121
|
-
and
|
133
|
+
and entity["dev_class"] == "thermostat"
|
122
134
|
):
|
123
|
-
thermostat =
|
124
|
-
sensors =
|
135
|
+
thermostat = entity["thermostat"]
|
136
|
+
sensors = entity["sensors"]
|
125
137
|
temp_dict: ActuatorData = {
|
126
138
|
"setpoint_low": thermostat["setpoint"],
|
127
139
|
"setpoint_high": MAX_SETPOINT,
|
@@ -133,53 +145,68 @@ class SmileData(SmileHelper):
|
|
133
145
|
}
|
134
146
|
thermostat.pop("setpoint")
|
135
147
|
temp_dict.update(thermostat)
|
136
|
-
|
148
|
+
entity["thermostat"] = temp_dict
|
137
149
|
if "setpoint" in sensors:
|
138
150
|
sensors.pop("setpoint")
|
139
151
|
sensors["setpoint_low"] = temp_dict["setpoint_low"]
|
140
152
|
sensors["setpoint_high"] = temp_dict["setpoint_high"]
|
141
|
-
self._count += 2
|
153
|
+
self._count += 2 # add 4, remove 2
|
154
|
+
|
155
|
+
|
156
|
+
def _get_location_data(self, loc_id: str) -> GwEntityData:
|
157
|
+
"""Helper-function for _all_entity_data() and async_update().
|
158
|
+
|
159
|
+
Provide entity-data, based on Location ID (= loc_id).
|
160
|
+
"""
|
161
|
+
zone = self._zones[loc_id]
|
162
|
+
data = self._get_zone_data(loc_id)
|
163
|
+
if ctrl_state := self._control_state(loc_id):
|
164
|
+
data["control_state"] = ctrl_state
|
165
|
+
self._count += 1
|
142
166
|
|
143
|
-
|
144
|
-
|
167
|
+
# Thermostat data (presets, temperatures etc)
|
168
|
+
self._climate_data(loc_id, zone, data)
|
169
|
+
|
170
|
+
return data
|
145
171
|
|
146
|
-
|
172
|
+
def _get_entity_data(self, entity_id: str) -> GwEntityData:
|
173
|
+
"""Helper-function for _update_gw_entities() and async_update().
|
174
|
+
|
175
|
+
Provide entity-data, based on appliance_id (= entity_id).
|
147
176
|
"""
|
148
|
-
|
149
|
-
data = self._get_measurement_data(
|
177
|
+
entity = self.gw_entities[entity_id]
|
178
|
+
data = self._get_measurement_data(entity_id)
|
150
179
|
|
151
|
-
# Check availability of wired-connected
|
180
|
+
# Check availability of wired-connected entities
|
152
181
|
# Smartmeter
|
153
182
|
self._check_availability(
|
154
|
-
|
183
|
+
entity, "smartmeter", data, "P1 does not seem to be connected"
|
155
184
|
)
|
156
|
-
# OpenTherm
|
157
|
-
if
|
185
|
+
# OpenTherm entity
|
186
|
+
if entity["name"] != "OnOff":
|
158
187
|
self._check_availability(
|
159
|
-
|
188
|
+
entity, "heater_central", data, "no OpenTherm communication"
|
160
189
|
)
|
161
190
|
|
162
191
|
# Switching groups data
|
163
|
-
self.
|
192
|
+
self._entity_switching_group(entity, data)
|
164
193
|
# Adam data
|
165
|
-
self.
|
166
|
-
# Skip obtaining data for (Adam) secondary thermostats
|
167
|
-
if device["dev_class"] not in ZONE_THERMOSTATS:
|
168
|
-
return data
|
194
|
+
self._get_adam_data(entity, data)
|
169
195
|
|
170
|
-
# Thermostat data (presets, temperatures etc)
|
171
|
-
self.
|
196
|
+
# Thermostat data for Anna (presets, temperatures etc)
|
197
|
+
if self.smile(ANNA) and entity["dev_class"] == "thermostat":
|
198
|
+
self._climate_data(entity_id, entity, data)
|
172
199
|
|
173
200
|
return data
|
174
201
|
|
175
202
|
def _check_availability(
|
176
|
-
self,
|
203
|
+
self, entity: GwEntityData, dev_class: str, data: GwEntityData, message: str
|
177
204
|
) -> None:
|
178
|
-
"""Helper-function for
|
205
|
+
"""Helper-function for _get_entity_data().
|
179
206
|
|
180
|
-
Provide availability status for the wired-
|
207
|
+
Provide availability status for the wired-connected devices.
|
181
208
|
"""
|
182
|
-
if
|
209
|
+
if entity["dev_class"] == dev_class:
|
183
210
|
data["available"] = True
|
184
211
|
self._count += 1
|
185
212
|
for item in self._notifications.values():
|
@@ -187,8 +214,8 @@ class SmileData(SmileHelper):
|
|
187
214
|
if message in msg:
|
188
215
|
data["available"] = False
|
189
216
|
|
190
|
-
def
|
191
|
-
"""Helper-function for
|
217
|
+
def _get_adam_data(self, entity: GwEntityData, data: GwEntityData) -> None:
|
218
|
+
"""Helper-function for _get_entity_data().
|
192
219
|
|
193
220
|
Determine Adam heating-status for on-off heating via valves,
|
194
221
|
available regulations_modes and thermostat control_states.
|
@@ -196,14 +223,14 @@ class SmileData(SmileHelper):
|
|
196
223
|
if self.smile(ADAM):
|
197
224
|
# Indicate heating_state based on valves being open in case of city-provided heating
|
198
225
|
if (
|
199
|
-
|
226
|
+
entity["dev_class"] == "heater_central"
|
200
227
|
and self._on_off_device
|
201
228
|
and isinstance(self._heating_valves(), int)
|
202
229
|
):
|
203
230
|
data["binary_sensors"]["heating_state"] = self._heating_valves() != 0
|
204
231
|
|
205
232
|
# Show the allowed regulation_modes and gateway_modes
|
206
|
-
if
|
233
|
+
if entity["dev_class"] == "gateway":
|
207
234
|
if self._reg_allowed_modes:
|
208
235
|
data["regulation_modes"] = self._reg_allowed_modes
|
209
236
|
self._count += 1
|
@@ -211,19 +238,20 @@ class SmileData(SmileHelper):
|
|
211
238
|
data["gateway_modes"] = self._gw_allowed_modes
|
212
239
|
self._count += 1
|
213
240
|
|
214
|
-
# Control_state, only available for Adam primary thermostats
|
215
|
-
if device["dev_class"] in ZONE_THERMOSTATS:
|
216
|
-
loc_id = device["location"]
|
217
|
-
if ctrl_state := self._control_state(loc_id):
|
218
|
-
data["control_state"] = ctrl_state
|
219
|
-
self._count += 1
|
220
241
|
|
221
|
-
def
|
222
|
-
|
242
|
+
def _climate_data(
|
243
|
+
self,
|
244
|
+
location_id: str,
|
245
|
+
entity: GwEntityData,
|
246
|
+
data: GwEntityData
|
247
|
+
) -> None:
|
248
|
+
"""Helper-function for _get_entity_data().
|
223
249
|
|
224
|
-
Determine climate-control
|
250
|
+
Determine climate-control entity data.
|
225
251
|
"""
|
226
|
-
loc_id =
|
252
|
+
loc_id = location_id
|
253
|
+
if entity.get("location") is not None:
|
254
|
+
loc_id = entity["location"]
|
227
255
|
|
228
256
|
# Presets
|
229
257
|
data["preset_modes"] = None
|
@@ -258,13 +286,13 @@ class SmileData(SmileHelper):
|
|
258
286
|
|
259
287
|
def check_reg_mode(self, mode: str) -> bool:
|
260
288
|
"""Helper-function for device_data_climate()."""
|
261
|
-
gateway = self.
|
289
|
+
gateway = self.gw_entities[self.gateway_id]
|
262
290
|
return (
|
263
291
|
"regulation_modes" in gateway and gateway["select_regulation_mode"] == mode
|
264
292
|
)
|
265
293
|
|
266
294
|
def _get_schedule_states_with_off(
|
267
|
-
self, location: str, schedules: list[str], selected: str, data:
|
295
|
+
self, location: str, schedules: list[str], selected: str, data: GwEntityData
|
268
296
|
) -> None:
|
269
297
|
"""Collect schedules with states for each thermostat.
|
270
298
|
|