plugwise 1.7.2__tar.gz → 1.7.3__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.7.2 → plugwise-1.7.3}/PKG-INFO +4 -3
- {plugwise-1.7.2 → plugwise-1.7.3}/README.md +3 -2
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/__init__.py +12 -19
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/common.py +19 -9
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/constants.py +0 -11
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/data.py +10 -21
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/helper.py +29 -18
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/legacy/data.py +2 -12
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/legacy/helper.py +28 -15
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/legacy/smile.py +36 -31
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/smile.py +71 -40
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/smilecomm.py +4 -2
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/util.py +26 -35
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise.egg-info/PKG-INFO +4 -3
- {plugwise-1.7.2 → plugwise-1.7.3}/pyproject.toml +1 -8
- {plugwise-1.7.2 → plugwise-1.7.3}/LICENSE +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/exceptions.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise/py.typed +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise.egg-info/SOURCES.txt +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise.egg-info/dependency_links.txt +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise.egg-info/requires.txt +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/plugwise.egg-info/top_level.txt +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/setup.cfg +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/setup.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_adam.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_anna.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_generic.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_init.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_legacy_anna.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_legacy_generic.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_legacy_p1.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_legacy_stretch.py +0 -0
- {plugwise-1.7.2 → plugwise-1.7.3}/tests/test_p1.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: plugwise
|
3
|
-
Version: 1.7.
|
3
|
+
Version: 1.7.3
|
4
4
|
Summary: Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3.
|
5
5
|
Home-page: https://github.com/plugwise/python-plugwise
|
6
6
|
Author: Plugwise device owners
|
@@ -48,12 +48,13 @@ Requires-Dist: python-dateutil
|
|
48
48
|
|
49
49
|
# Plugwise python module
|
50
50
|
|
51
|
-
This module is the backend for the [`plugwise` component](https://github.com/home-assistant/core/tree/dev/homeassistant/components/plugwise) in Home Assistant Core (which we maintain as
|
51
|
+
This module is the backend for the [`plugwise` component](https://github.com/home-assistant/core/tree/dev/homeassistant/components/plugwise) in Home Assistant Core (which we maintain as code owners).
|
52
52
|
|
53
|
-
This module supports `Smile`s
|
53
|
+
This module supports Hubs such as `Adam`, `Smile`s for Anna and P1 and `Stretch`, i.e. the networked plugwise devices. For the USB (or Stick-standalone version) please refer to upcoming [`plugwise-usb` component](https://github.com/plugwise/plugwise_usb-beta).
|
54
54
|
|
55
55
|
Our main usage for this module is supporting [Home Assistant](https://www.home-assistant.io) / [home-assistant](http://github.com/home-assistant/core/)
|
56
56
|
|
57
|
+

|
57
58
|
[](https://github.com/plugwise)
|
58
59
|
[](https://coderabbit.ai)
|
59
60
|
[](https://github.com/plugwise/python-plugwise/issues/291)
|
@@ -1,11 +1,12 @@
|
|
1
1
|
# Plugwise python module
|
2
2
|
|
3
|
-
This module is the backend for the [`plugwise` component](https://github.com/home-assistant/core/tree/dev/homeassistant/components/plugwise) in Home Assistant Core (which we maintain as
|
3
|
+
This module is the backend for the [`plugwise` component](https://github.com/home-assistant/core/tree/dev/homeassistant/components/plugwise) in Home Assistant Core (which we maintain as code owners).
|
4
4
|
|
5
|
-
This module supports `Smile`s
|
5
|
+
This module supports Hubs such as `Adam`, `Smile`s for Anna and P1 and `Stretch`, i.e. the networked plugwise devices. For the USB (or Stick-standalone version) please refer to upcoming [`plugwise-usb` component](https://github.com/plugwise/plugwise_usb-beta).
|
6
6
|
|
7
7
|
Our main usage for this module is supporting [Home Assistant](https://www.home-assistant.io) / [home-assistant](http://github.com/home-assistant/core/)
|
8
8
|
|
9
|
+

|
9
10
|
[](https://github.com/plugwise)
|
10
11
|
[](https://coderabbit.ai)
|
11
12
|
[](https://github.com/plugwise/python-plugwise/issues/291)
|
@@ -18,7 +18,6 @@ from plugwise.constants import (
|
|
18
18
|
STATUS,
|
19
19
|
SYSTEM,
|
20
20
|
GwEntityData,
|
21
|
-
SmileProps,
|
22
21
|
ThermoLoc,
|
23
22
|
)
|
24
23
|
from plugwise.exceptions import (
|
@@ -69,7 +68,6 @@ class Smile(SmileComm):
|
|
69
68
|
self._opentherm_device = False
|
70
69
|
self._schedule_old_states: dict[str, dict[str, str]] = {}
|
71
70
|
self._smile_api: SmileAPI | SmileLegacyAPI
|
72
|
-
self._smile_props: SmileProps = {}
|
73
71
|
self._stretch_v2 = False
|
74
72
|
self._target_smile: str = NONE
|
75
73
|
self.smile_hostname: str = NONE
|
@@ -86,26 +84,22 @@ class Smile(SmileComm):
|
|
86
84
|
@property
|
87
85
|
def cooling_present(self) -> bool:
|
88
86
|
"""Return the cooling capability."""
|
89
|
-
|
90
|
-
return self._smile_props["cooling_present"]
|
91
|
-
return False
|
87
|
+
return self._smile_api.cooling_present
|
92
88
|
|
93
89
|
@property
|
94
90
|
def gateway_id(self) -> str:
|
95
91
|
"""Return the gateway-id."""
|
96
|
-
return self.
|
92
|
+
return self._smile_api.gateway_id
|
97
93
|
|
98
94
|
@property
|
99
95
|
def heater_id(self) -> str:
|
100
96
|
"""Return the heater-id."""
|
101
|
-
|
102
|
-
return self._smile_props["heater_id"]
|
103
|
-
return NONE
|
97
|
+
return self._smile_api.heater_id
|
104
98
|
|
105
99
|
@property
|
106
100
|
def item_count(self) -> int:
|
107
101
|
"""Return the item-count."""
|
108
|
-
return self.
|
102
|
+
return self._smile_api.item_count
|
109
103
|
|
110
104
|
@property
|
111
105
|
def reboot(self) -> bool:
|
@@ -162,7 +156,6 @@ class Smile(SmileComm):
|
|
162
156
|
self._opentherm_device,
|
163
157
|
self._request,
|
164
158
|
self._schedule_old_states,
|
165
|
-
self._smile_props,
|
166
159
|
self.smile_hostname,
|
167
160
|
self.smile_hw_version,
|
168
161
|
self.smile_mac_address,
|
@@ -179,7 +172,6 @@ class Smile(SmileComm):
|
|
179
172
|
self._on_off_device,
|
180
173
|
self._opentherm_device,
|
181
174
|
self._request,
|
182
|
-
self._smile_props,
|
183
175
|
self._stretch_v2,
|
184
176
|
self._target_smile,
|
185
177
|
self.smile_hostname,
|
@@ -198,7 +190,9 @@ class Smile(SmileComm):
|
|
198
190
|
|
199
191
|
return self.smile_version
|
200
192
|
|
201
|
-
async def _smile_detect(
|
193
|
+
async def _smile_detect(
|
194
|
+
self, result: etree.Element, dsmrmain: etree.Element
|
195
|
+
) -> None:
|
202
196
|
"""Helper-function for connect().
|
203
197
|
|
204
198
|
Detect which type of Plugwise Gateway is being connected.
|
@@ -256,10 +250,8 @@ class Smile(SmileComm):
|
|
256
250
|
# For Adam, Anna, determine the system capabilities:
|
257
251
|
# Find the connected heating/cooling device (heater_central),
|
258
252
|
# e.g. heat-pump or gas-fired heater
|
259
|
-
onoff_boiler
|
260
|
-
open_therm_boiler
|
261
|
-
"./module/protocols/open_therm_boiler"
|
262
|
-
)
|
253
|
+
onoff_boiler = result.find("./module/protocols/onoff_boiler")
|
254
|
+
open_therm_boiler = result.find("./module/protocols/open_therm_boiler")
|
263
255
|
self._on_off_device = onoff_boiler is not None
|
264
256
|
self._opentherm_device = open_therm_boiler is not None
|
265
257
|
|
@@ -272,7 +264,7 @@ class Smile(SmileComm):
|
|
272
264
|
self._elga = True
|
273
265
|
|
274
266
|
async def _smile_detect_legacy(
|
275
|
-
self, result: etree, dsmrmain: etree, model: str
|
267
|
+
self, result: etree.Element, dsmrmain: etree.Element, model: str
|
276
268
|
) -> str:
|
277
269
|
"""Helper-function for _smile_detect().
|
278
270
|
|
@@ -298,11 +290,12 @@ class Smile(SmileComm):
|
|
298
290
|
self.smile_version = parse(system.find("./gateway/firmware").text)
|
299
291
|
return_model = str(system.find("./gateway/product").text)
|
300
292
|
self.smile_hostname = system.find("./gateway/hostname").text
|
301
|
-
# If wlan0 contains data it's active,
|
293
|
+
# If wlan0 contains data it's active, eth0 should be checked last as is preferred
|
302
294
|
for network in ("wlan0", "eth0"):
|
303
295
|
locator = f"./{network}/mac"
|
304
296
|
if (net_locator := system.find(locator)) is not None:
|
305
297
|
self.smile_mac_address = net_locator.text
|
298
|
+
|
306
299
|
# P1 legacy:
|
307
300
|
elif dsmrmain is not None:
|
308
301
|
status = await self._request(STATUS)
|
@@ -27,7 +27,9 @@ from defusedxml import ElementTree as etree
|
|
27
27
|
from munch import Munch
|
28
28
|
|
29
29
|
|
30
|
-
def get_zigbee_data(
|
30
|
+
def get_zigbee_data(
|
31
|
+
module: etree.Element, module_data: ModuleData, legacy: bool
|
32
|
+
) -> None:
|
31
33
|
"""Helper-function for _get_module_data()."""
|
32
34
|
if legacy:
|
33
35
|
# Stretches
|
@@ -49,13 +51,18 @@ class SmileCommon:
|
|
49
51
|
"""Init."""
|
50
52
|
self._cooling_present: bool
|
51
53
|
self._count: int
|
52
|
-
self._domain_objects: etree
|
54
|
+
self._domain_objects: etree.Element
|
53
55
|
self._heater_id: str = NONE
|
54
56
|
self._on_off_device: bool
|
55
57
|
self.gw_entities: dict[str, GwEntityData] = {}
|
56
58
|
self.smile_name: str
|
57
59
|
self.smile_type: str
|
58
60
|
|
61
|
+
@property
|
62
|
+
def heater_id(self) -> str:
|
63
|
+
"""Return the heater-id."""
|
64
|
+
return self._heater_id
|
65
|
+
|
59
66
|
def smile(self, name: str) -> bool:
|
60
67
|
"""Helper-function checking the smile-name."""
|
61
68
|
return self.smile_name == name
|
@@ -63,10 +70,10 @@ class SmileCommon:
|
|
63
70
|
def _appl_heater_central_info(
|
64
71
|
self,
|
65
72
|
appl: Munch,
|
66
|
-
xml_1: etree,
|
73
|
+
xml_1: etree.Element,
|
67
74
|
legacy: bool,
|
68
|
-
xml_2: etree = None,
|
69
|
-
xml_3: etree = None,
|
75
|
+
xml_2: etree.Element = None,
|
76
|
+
xml_3: etree.Element = None,
|
70
77
|
) -> Munch:
|
71
78
|
"""Helper-function for _appliance_info_finder()."""
|
72
79
|
# Find the valid heater_central
|
@@ -74,6 +81,9 @@ class SmileCommon:
|
|
74
81
|
xml_2 = return_valid(xml_2, self._domain_objects)
|
75
82
|
self._heater_id = check_heater_central(xml_2)
|
76
83
|
|
84
|
+
if self._heater_id == NONE:
|
85
|
+
return Munch() # pragma: no cover
|
86
|
+
|
77
87
|
# Info for On-Off device
|
78
88
|
if self._on_off_device:
|
79
89
|
appl.name = "OnOff" # pragma: no cover
|
@@ -101,7 +111,7 @@ class SmileCommon:
|
|
101
111
|
return appl
|
102
112
|
|
103
113
|
def _appl_thermostat_info(
|
104
|
-
self, appl: Munch, xml_1: etree, xml_2: etree = None
|
114
|
+
self, appl: Munch, xml_1: etree.Element, xml_2: etree.Element = None
|
105
115
|
) -> Munch:
|
106
116
|
"""Helper-function for _appliance_info_finder()."""
|
107
117
|
locator = "./logs/point_log[type='thermostat']/thermostat"
|
@@ -190,7 +200,7 @@ class SmileCommon:
|
|
190
200
|
return switch_groups
|
191
201
|
|
192
202
|
def _get_lock_state(
|
193
|
-
self, xml: etree, data: GwEntityData, stretch_v2: bool = False
|
203
|
+
self, xml: etree.Element, data: GwEntityData, stretch_v2: bool = False
|
194
204
|
) -> None:
|
195
205
|
"""Helper-function for _get_measurement_data().
|
196
206
|
|
@@ -209,9 +219,9 @@ class SmileCommon:
|
|
209
219
|
|
210
220
|
def _get_module_data(
|
211
221
|
self,
|
212
|
-
xml_1: etree,
|
222
|
+
xml_1: etree.Element,
|
213
223
|
locator: str,
|
214
|
-
xml_2: etree = None,
|
224
|
+
xml_2: etree.Element = None,
|
215
225
|
legacy: bool = False,
|
216
226
|
) -> ModuleData:
|
217
227
|
"""Helper-function for _energy_device_info_finder() and _appliance_info_finder().
|
@@ -393,17 +393,6 @@ ZONE_THERMOSTATS: Final[tuple[str, ...]] = (
|
|
393
393
|
)
|
394
394
|
|
395
395
|
|
396
|
-
class SmileProps(TypedDict, total=False):
|
397
|
-
"""The SmileProps Data class."""
|
398
|
-
|
399
|
-
cooling_present: bool
|
400
|
-
gateway_id: str
|
401
|
-
heater_id: str
|
402
|
-
item_count: int
|
403
|
-
reboot: bool
|
404
|
-
smile_name: str
|
405
|
-
|
406
|
-
|
407
396
|
class ModuleData(TypedDict):
|
408
397
|
"""The Module data class."""
|
409
398
|
|
@@ -16,7 +16,6 @@ from plugwise.constants import (
|
|
16
16
|
OFF,
|
17
17
|
ActuatorData,
|
18
18
|
GwEntityData,
|
19
|
-
SmileProps,
|
20
19
|
)
|
21
20
|
from plugwise.helper import SmileHelper
|
22
21
|
from plugwise.util import remove_empty_platform_dicts
|
@@ -27,28 +26,19 @@ class SmileData(SmileHelper):
|
|
27
26
|
|
28
27
|
def __init__(self) -> None:
|
29
28
|
"""Init."""
|
30
|
-
|
29
|
+
super().__init__()
|
31
30
|
self._zones: dict[str, GwEntityData] = {}
|
32
|
-
SmileHelper.__init__(self)
|
33
31
|
|
34
32
|
def _all_entity_data(self) -> None:
|
35
33
|
"""Helper-function for get_all_gateway_entities().
|
36
34
|
|
37
|
-
Collect data for each entity and add to self.
|
35
|
+
Collect data for each entity and add to self.gw_entities.
|
38
36
|
"""
|
39
37
|
self._update_gw_entities()
|
40
38
|
if self.smile(ADAM):
|
41
39
|
self._update_zones()
|
42
40
|
self.gw_entities.update(self._zones)
|
43
41
|
|
44
|
-
self._smile_props["gateway_id"] = self._gateway_id
|
45
|
-
self._smile_props["item_count"] = self._count
|
46
|
-
self._smile_props["reboot"] = True
|
47
|
-
self._smile_props["smile_name"] = self.smile_name
|
48
|
-
if self._is_thermostat:
|
49
|
-
self._smile_props["heater_id"] = self._heater_id
|
50
|
-
self._smile_props["cooling_present"] = self._cooling_present
|
51
|
-
|
52
42
|
def _update_zones(self) -> None:
|
53
43
|
"""Helper-function for _all_entity_data() and async_update().
|
54
44
|
|
@@ -228,6 +218,7 @@ class SmileData(SmileHelper):
|
|
228
218
|
for msg in item.values():
|
229
219
|
if message in msg:
|
230
220
|
data["available"] = False
|
221
|
+
break
|
231
222
|
|
232
223
|
def _get_adam_data(self, entity: GwEntityData, data: GwEntityData) -> None:
|
233
224
|
"""Helper-function for _get_entity_data().
|
@@ -329,16 +320,14 @@ class SmileData(SmileHelper):
|
|
329
320
|
|
330
321
|
Also, replace NONE by OFF when none of the schedules are active.
|
331
322
|
"""
|
332
|
-
loc_schedule_states: dict[str, str] = {}
|
333
|
-
for schedule in schedules:
|
334
|
-
loc_schedule_states[schedule] = "off"
|
335
|
-
if schedule == selected and data["climate_mode"] == "auto":
|
336
|
-
loc_schedule_states[schedule] = "on"
|
337
|
-
self._schedule_old_states[location] = loc_schedule_states
|
338
|
-
|
339
323
|
all_off = True
|
340
|
-
|
341
|
-
|
324
|
+
self._schedule_old_states[location] = {}
|
325
|
+
for schedule in schedules:
|
326
|
+
active: bool = schedule == selected and data["climate_mode"] == "auto"
|
327
|
+
self._schedule_old_states[location][schedule] = "off"
|
328
|
+
if active:
|
329
|
+
self._schedule_old_states[location][schedule] = "on"
|
342
330
|
all_off = False
|
331
|
+
|
343
332
|
if all_off:
|
344
333
|
data["select_schedule"] = OFF
|
@@ -59,7 +59,9 @@ from munch import Munch
|
|
59
59
|
from packaging import version
|
60
60
|
|
61
61
|
|
62
|
-
def search_actuator_functionalities(
|
62
|
+
def search_actuator_functionalities(
|
63
|
+
appliance: etree.Element, actuator: str
|
64
|
+
) -> etree.Element | None:
|
63
65
|
"""Helper-function for finding the relevant actuator xml-structure."""
|
64
66
|
locator = f"./actuator_functionalities/{actuator}"
|
65
67
|
if (search := appliance.find(locator)) is not None:
|
@@ -73,6 +75,7 @@ class SmileHelper(SmileCommon):
|
|
73
75
|
|
74
76
|
def __init__(self) -> None:
|
75
77
|
"""Set the constructor for this class."""
|
78
|
+
super().__init__()
|
76
79
|
self._endpoint: str
|
77
80
|
self._elga: bool
|
78
81
|
self._is_thermostat: bool
|
@@ -87,7 +90,16 @@ class SmileHelper(SmileCommon):
|
|
87
90
|
self.smile_model: str
|
88
91
|
self.smile_model_id: str | None
|
89
92
|
self.smile_version: version.Version
|
90
|
-
|
93
|
+
|
94
|
+
@property
|
95
|
+
def gateway_id(self) -> str:
|
96
|
+
"""Return the gateway-id."""
|
97
|
+
return self._gateway_id
|
98
|
+
|
99
|
+
@property
|
100
|
+
def item_count(self) -> int:
|
101
|
+
"""Return the item-count."""
|
102
|
+
return self._count
|
91
103
|
|
92
104
|
def _all_appliances(self) -> None:
|
93
105
|
"""Collect all appliances with relevant info.
|
@@ -195,6 +207,7 @@ class SmileHelper(SmileCommon):
|
|
195
207
|
other_entities = self.gw_entities
|
196
208
|
priority_entities = {entity_id: priority_entity}
|
197
209
|
self.gw_entities = {**priority_entities, **other_entities}
|
210
|
+
break
|
198
211
|
|
199
212
|
def _all_locations(self) -> None:
|
200
213
|
"""Collect all locations."""
|
@@ -212,7 +225,7 @@ class SmileHelper(SmileCommon):
|
|
212
225
|
f"./location[@id='{loc.loc_id}']"
|
213
226
|
)
|
214
227
|
|
215
|
-
def _appliance_info_finder(self, appl: Munch, appliance: etree) -> Munch:
|
228
|
+
def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch:
|
216
229
|
"""Collect info for all appliances found."""
|
217
230
|
match appl.pwclass:
|
218
231
|
case "gateway":
|
@@ -230,7 +243,7 @@ class SmileHelper(SmileCommon):
|
|
230
243
|
appliance, "domestic_hot_water_mode_control_functionality"
|
231
244
|
)
|
232
245
|
# Skip orphaned heater_central (Core Issue #104433)
|
233
|
-
if appl.entity_id != self.
|
246
|
+
if appl.entity_id != self.heater_id:
|
234
247
|
return Munch()
|
235
248
|
return appl
|
236
249
|
case _ as s if s.endswith("_plug"):
|
@@ -250,9 +263,9 @@ class SmileHelper(SmileCommon):
|
|
250
263
|
appl.zigbee_mac = module_data["zigbee_mac_address"]
|
251
264
|
return appl
|
252
265
|
case _: # pragma: no cover
|
253
|
-
return
|
266
|
+
return Munch()
|
254
267
|
|
255
|
-
def _appl_gateway_info(self, appl: Munch, appliance: etree) -> Munch:
|
268
|
+
def _appl_gateway_info(self, appl: Munch, appliance: etree.Element) -> Munch:
|
256
269
|
"""Helper-function for _appliance_info_finder()."""
|
257
270
|
self._gateway_id = appliance.attrib["id"]
|
258
271
|
appl.firmware = str(self.smile_version)
|
@@ -285,7 +298,7 @@ class SmileHelper(SmileCommon):
|
|
285
298
|
return appl
|
286
299
|
|
287
300
|
def _get_appl_actuator_modes(
|
288
|
-
self, appliance: etree, actuator_type: str
|
301
|
+
self, appliance: etree.Element, actuator_type: str
|
289
302
|
) -> list[str]:
|
290
303
|
"""Get allowed modes for the given actuator type."""
|
291
304
|
mode_list: list[str] = []
|
@@ -341,7 +354,7 @@ class SmileHelper(SmileCommon):
|
|
341
354
|
|
342
355
|
# Get non-P1 data from APPLIANCES
|
343
356
|
measurements = DEVICE_MEASUREMENTS
|
344
|
-
if self._is_thermostat and entity_id == self.
|
357
|
+
if self._is_thermostat and entity_id == self.heater_id:
|
345
358
|
measurements = HEATER_CENTRAL_MEASUREMENTS
|
346
359
|
# Show the allowed dhw_modes (Loria only)
|
347
360
|
if self._dhw_allowed_modes:
|
@@ -397,7 +410,7 @@ class SmileHelper(SmileCommon):
|
|
397
410
|
|
398
411
|
def _appliance_measurements(
|
399
412
|
self,
|
400
|
-
appliance: etree,
|
413
|
+
appliance: etree.Element,
|
401
414
|
data: GwEntityData,
|
402
415
|
measurements: dict[str, DATA | UOM],
|
403
416
|
) -> None:
|
@@ -429,7 +442,7 @@ class SmileHelper(SmileCommon):
|
|
429
442
|
self._count = count_data_items(self._count, data)
|
430
443
|
|
431
444
|
def _get_toggle_state(
|
432
|
-
self, xml: etree, toggle: str, name: ToggleNameType, data: GwEntityData
|
445
|
+
self, xml: etree.Element, toggle: str, name: ToggleNameType, data: GwEntityData
|
433
446
|
) -> None:
|
434
447
|
"""Helper-function for _get_measurement_data().
|
435
448
|
|
@@ -458,7 +471,7 @@ class SmileHelper(SmileCommon):
|
|
458
471
|
)
|
459
472
|
|
460
473
|
def _get_actuator_functionalities(
|
461
|
-
self, xml: etree, entity: GwEntityData, data: GwEntityData
|
474
|
+
self, xml: etree.Element, entity: GwEntityData, data: GwEntityData
|
462
475
|
) -> None:
|
463
476
|
"""Get and process the actuator_functionalities details for an entity.
|
464
477
|
|
@@ -520,7 +533,7 @@ class SmileHelper(SmileCommon):
|
|
520
533
|
data[act_item] = temp_dict
|
521
534
|
|
522
535
|
def _get_actuator_mode(
|
523
|
-
self, appliance: etree, entity_id: str, key: str
|
536
|
+
self, appliance: etree.Element, entity_id: str, key: str
|
524
537
|
) -> str | None:
|
525
538
|
"""Helper-function for _get_regulation_mode and _get_gateway_mode.
|
526
539
|
|
@@ -535,7 +548,7 @@ class SmileHelper(SmileCommon):
|
|
535
548
|
return None
|
536
549
|
|
537
550
|
def _get_regulation_mode(
|
538
|
-
self, appliance: etree, entity_id: str, data: GwEntityData
|
551
|
+
self, appliance: etree.Element, entity_id: str, data: GwEntityData
|
539
552
|
) -> None:
|
540
553
|
"""Helper-function for _get_measurement_data().
|
541
554
|
|
@@ -551,7 +564,7 @@ class SmileHelper(SmileCommon):
|
|
551
564
|
self._cooling_enabled = mode == "cooling"
|
552
565
|
|
553
566
|
def _get_gateway_mode(
|
554
|
-
self, appliance: etree, entity_id: str, data: GwEntityData
|
567
|
+
self, appliance: etree.Element, entity_id: str, data: GwEntityData
|
555
568
|
) -> None:
|
556
569
|
"""Helper-function for _get_measurement_data().
|
557
570
|
|
@@ -616,7 +629,7 @@ class SmileHelper(SmileCommon):
|
|
616
629
|
|
617
630
|
Support added for Techneco Elga and Thercon Loria/Thermastage.
|
618
631
|
"""
|
619
|
-
if entity_id != self.
|
632
|
+
if entity_id != self.heater_id:
|
620
633
|
return
|
621
634
|
|
622
635
|
if "elga_status_code" in data:
|
@@ -837,9 +850,7 @@ class SmileHelper(SmileCommon):
|
|
837
850
|
return presets # pragma: no cover
|
838
851
|
|
839
852
|
for rule_id in rule_ids:
|
840
|
-
directives
|
841
|
-
f'rule[@id="{rule_id}"]/directives'
|
842
|
-
)
|
853
|
+
directives = self._domain_objects.find(f'rule[@id="{rule_id}"]/directives')
|
843
854
|
for directive in directives:
|
844
855
|
preset = directive.find("then").attrib
|
845
856
|
presets[directive.attrib["preset"]] = [
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
7
7
|
|
8
8
|
# Dict as class
|
9
9
|
# Version detection
|
10
|
-
from plugwise.constants import NONE, OFF, GwEntityData
|
10
|
+
from plugwise.constants import NONE, OFF, GwEntityData
|
11
11
|
from plugwise.legacy.helper import SmileLegacyHelper
|
12
12
|
from plugwise.util import remove_empty_platform_dicts
|
13
13
|
|
@@ -15,22 +15,12 @@ from plugwise.util import remove_empty_platform_dicts
|
|
15
15
|
class SmileLegacyData(SmileLegacyHelper):
|
16
16
|
"""The Plugwise Smile main class."""
|
17
17
|
|
18
|
-
def __init__(self) -> None:
|
19
|
-
"""Init."""
|
20
|
-
self._smile_props: SmileProps
|
21
|
-
SmileLegacyHelper.__init__(self)
|
22
|
-
|
23
18
|
def _all_entity_data(self) -> None:
|
24
19
|
"""Helper-function for get_all_gateway_entities().
|
25
20
|
|
26
|
-
Collect data for each entity and add to self.
|
21
|
+
Collect data for each entity and add to self.gw_entities.
|
27
22
|
"""
|
28
23
|
self._update_gw_entities()
|
29
|
-
self._smile_props["gateway_id"] = self.gateway_id
|
30
|
-
self._smile_props["item_count"] = self._count
|
31
|
-
self._smile_props["smile_name"] = self.smile_name
|
32
|
-
if self._is_thermostat:
|
33
|
-
self._smile_props["heater_id"] = self._heater_id
|
34
24
|
|
35
25
|
def _update_gw_entities(self) -> None:
|
36
26
|
"""Helper-function for _all_entity_data() and async_update().
|
@@ -23,6 +23,7 @@ from plugwise.constants import (
|
|
23
23
|
NONE,
|
24
24
|
OFF,
|
25
25
|
P1_LEGACY_MEASUREMENTS,
|
26
|
+
PRIORITY_DEVICE_CLASSES,
|
26
27
|
TEMP_CELSIUS,
|
27
28
|
THERMOSTAT_CLASSES,
|
28
29
|
UOM,
|
@@ -49,7 +50,7 @@ from munch import Munch
|
|
49
50
|
from packaging.version import Version
|
50
51
|
|
51
52
|
|
52
|
-
def etree_to_dict(element: etree) -> dict[str, str]:
|
53
|
+
def etree_to_dict(element: etree.Element) -> dict[str, str]:
|
53
54
|
"""Helper-function translating xml Element to dict."""
|
54
55
|
node: dict[str, str] = {}
|
55
56
|
if element is not None:
|
@@ -63,18 +64,29 @@ class SmileLegacyHelper(SmileCommon):
|
|
63
64
|
|
64
65
|
def __init__(self) -> None:
|
65
66
|
"""Set the constructor for this class."""
|
66
|
-
|
67
|
+
super().__init__()
|
68
|
+
self._appliances: etree.Element
|
69
|
+
self._gateway_id: str = NONE
|
67
70
|
self._is_thermostat: bool
|
68
71
|
self._loc_data: dict[str, ThermoLoc]
|
69
|
-
self._locations: etree
|
70
|
-
self._modules: etree
|
72
|
+
self._locations: etree.Element
|
73
|
+
self._modules: etree.Element
|
71
74
|
self._stretch_v2: bool
|
72
75
|
self.gw_entities: dict[str, GwEntityData] = {}
|
73
76
|
self.smile_mac_address: str | None
|
74
77
|
self.smile_model: str
|
75
78
|
self.smile_version: Version
|
76
79
|
self.smile_zigbee_mac_address: str | None
|
77
|
-
|
80
|
+
|
81
|
+
@property
|
82
|
+
def gateway_id(self) -> str:
|
83
|
+
"""Return the gateway-id."""
|
84
|
+
return self._gateway_id
|
85
|
+
|
86
|
+
@property
|
87
|
+
def item_count(self) -> int:
|
88
|
+
"""Return the item-count."""
|
89
|
+
return self._count
|
78
90
|
|
79
91
|
def _all_appliances(self) -> None:
|
80
92
|
"""Collect all appliances with relevant info."""
|
@@ -124,13 +136,13 @@ class SmileLegacyHelper(SmileCommon):
|
|
124
136
|
continue
|
125
137
|
|
126
138
|
# Skip orphaned heater_central (Core Issue #104433)
|
127
|
-
if appl.pwclass == "heater_central" and appl.entity_id != self.
|
139
|
+
if appl.pwclass == "heater_central" and appl.entity_id != self.heater_id:
|
128
140
|
continue # pragma: no cover
|
129
141
|
|
130
142
|
self._create_gw_entities(appl)
|
131
143
|
|
132
144
|
# Place the gateway and optional heater_central devices as 1st and 2nd
|
133
|
-
for dev_class in
|
145
|
+
for dev_class in PRIORITY_DEVICE_CLASSES:
|
134
146
|
for entity_id, entity in dict(self.gw_entities).items():
|
135
147
|
if entity["dev_class"] == dev_class:
|
136
148
|
tmp_entity = entity
|
@@ -138,6 +150,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
138
150
|
cleared_dict = self.gw_entities
|
139
151
|
add_to_front = {entity_id: tmp_entity}
|
140
152
|
self.gw_entities = {**add_to_front, **cleared_dict}
|
153
|
+
break
|
141
154
|
|
142
155
|
def _all_locations(self) -> None:
|
143
156
|
"""Collect all locations."""
|
@@ -171,11 +184,11 @@ class SmileLegacyHelper(SmileCommon):
|
|
171
184
|
|
172
185
|
Use the home_location or FAKE_APPL as entity id.
|
173
186
|
"""
|
174
|
-
self.
|
187
|
+
self._gateway_id = self._home_loc_id
|
175
188
|
if self.smile_type == "power":
|
176
|
-
self.
|
189
|
+
self._gateway_id = FAKE_APPL
|
177
190
|
|
178
|
-
self.gw_entities[self.
|
191
|
+
self.gw_entities[self._gateway_id] = {"dev_class": "gateway"}
|
179
192
|
self._count += 1
|
180
193
|
for key, value in {
|
181
194
|
"firmware": str(self.smile_version),
|
@@ -188,7 +201,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
188
201
|
}.items():
|
189
202
|
if value is not None:
|
190
203
|
gw_key = cast(ApplianceType, key)
|
191
|
-
self.gw_entities[self.
|
204
|
+
self.gw_entities[self._gateway_id][gw_key] = value
|
192
205
|
self._count += 1
|
193
206
|
|
194
207
|
def _appliance_info_finder(self, appliance: etree, appl: Munch) -> Munch:
|
@@ -266,7 +279,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
266
279
|
return data
|
267
280
|
|
268
281
|
measurements = DEVICE_MEASUREMENTS
|
269
|
-
if self._is_thermostat and entity_id == self.
|
282
|
+
if self._is_thermostat and entity_id == self.heater_id:
|
270
283
|
measurements = HEATER_CENTRAL_MEASUREMENTS
|
271
284
|
|
272
285
|
if (
|
@@ -280,7 +293,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
280
293
|
|
281
294
|
# Anna: the Smile outdoor_temperature is present in the Home location
|
282
295
|
# For some Anna's LOCATIONS is empty, falling back to domain_objects!
|
283
|
-
if self._is_thermostat and entity_id == self.
|
296
|
+
if self._is_thermostat and entity_id == self._gateway_id:
|
284
297
|
locator = f"./location[@id='{self._home_loc_id}']/logs/point_log[type='outdoor_temperature']/period/measurement"
|
285
298
|
if (found := self._domain_objects.find(locator)) is not None:
|
286
299
|
value = format_measure(found.text, NONE)
|
@@ -316,7 +329,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
316
329
|
|
317
330
|
def _appliance_measurements(
|
318
331
|
self,
|
319
|
-
appliance: etree,
|
332
|
+
appliance: etree.Element,
|
320
333
|
data: GwEntityData,
|
321
334
|
measurements: dict[str, DATA | UOM],
|
322
335
|
) -> None:
|
@@ -345,7 +358,7 @@ class SmileLegacyHelper(SmileCommon):
|
|
345
358
|
self._count = count_data_items(self._count, data)
|
346
359
|
|
347
360
|
def _get_actuator_functionalities(
|
348
|
-
self, xml: etree, entity: GwEntityData, data: GwEntityData
|
361
|
+
self, xml: etree.Element, entity: GwEntityData, data: GwEntityData
|
349
362
|
) -> None:
|
350
363
|
"""Helper-function for _get_measurement_data()."""
|
351
364
|
for item in ACTIVE_ACTUATORS:
|