plugwise 1.6.4__py3-none-any.whl → 1.7.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 +61 -53
- plugwise/common.py +20 -118
- plugwise/constants.py +3 -12
- plugwise/data.py +16 -17
- plugwise/helper.py +48 -257
- plugwise/legacy/data.py +7 -12
- plugwise/legacy/helper.py +19 -50
- plugwise/legacy/smile.py +30 -45
- plugwise/smile.py +29 -44
- plugwise/smilecomm.py +148 -0
- plugwise/util.py +112 -21
- {plugwise-1.6.4.dist-info → plugwise-1.7.0.dist-info}/METADATA +2 -2
- plugwise-1.7.0.dist-info/RECORD +18 -0
- {plugwise-1.6.4.dist-info → plugwise-1.7.0.dist-info}/WHEEL +1 -1
- plugwise-1.6.4.dist-info/RECORD +0 -17
- {plugwise-1.6.4.dist-info → plugwise-1.7.0.dist-info}/LICENSE +0 -0
- {plugwise-1.6.4.dist-info → plugwise-1.7.0.dist-info}/top_level.txt +0 -0
plugwise/__init__.py
CHANGED
@@ -17,7 +17,8 @@ from plugwise.constants import (
|
|
17
17
|
SMILES,
|
18
18
|
STATUS,
|
19
19
|
SYSTEM,
|
20
|
-
|
20
|
+
GwEntityData,
|
21
|
+
SmileProps,
|
21
22
|
ThermoLoc,
|
22
23
|
)
|
23
24
|
from plugwise.exceptions import (
|
@@ -28,9 +29,9 @@ from plugwise.exceptions import (
|
|
28
29
|
ResponseError,
|
29
30
|
UnsupportedDeviceError,
|
30
31
|
)
|
31
|
-
from plugwise.helper import SmileComm
|
32
32
|
from plugwise.legacy.smile import SmileLegacyAPI
|
33
33
|
from plugwise.smile import SmileAPI
|
34
|
+
from plugwise.smilecomm import SmileComm
|
34
35
|
|
35
36
|
import aiohttp
|
36
37
|
from defusedxml import ElementTree as etree
|
@@ -38,9 +39,7 @@ from packaging.version import Version, parse
|
|
38
39
|
|
39
40
|
|
40
41
|
class Smile(SmileComm):
|
41
|
-
"""The Plugwise
|
42
|
-
|
43
|
-
# pylint: disable=too-many-instance-attributes, too-many-public-methods
|
42
|
+
"""The main Plugwise Smile API class."""
|
44
43
|
|
45
44
|
def __init__(
|
46
45
|
self,
|
@@ -51,19 +50,14 @@ class Smile(SmileComm):
|
|
51
50
|
username: str = DEFAULT_USERNAME,
|
52
51
|
) -> None:
|
53
52
|
"""Set the constructor for this class."""
|
54
|
-
self._host = host
|
55
|
-
self._password = password
|
56
|
-
self._port = port
|
57
53
|
self._timeout = DEFAULT_LEGACY_TIMEOUT
|
58
|
-
self._username = username
|
59
|
-
self._websession = websession
|
60
54
|
super().__init__(
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
host,
|
56
|
+
password,
|
57
|
+
port,
|
64
58
|
self._timeout,
|
65
|
-
|
66
|
-
|
59
|
+
username,
|
60
|
+
websession,
|
67
61
|
)
|
68
62
|
|
69
63
|
self._cooling_present = False
|
@@ -75,10 +69,9 @@ class Smile(SmileComm):
|
|
75
69
|
self._opentherm_device = False
|
76
70
|
self._schedule_old_states: dict[str, dict[str, str]] = {}
|
77
71
|
self._smile_api: SmileAPI | SmileLegacyAPI
|
72
|
+
self._smile_props: SmileProps = {}
|
78
73
|
self._stretch_v2 = False
|
79
74
|
self._target_smile: str = NONE
|
80
|
-
self.gateway_id: str = NONE
|
81
|
-
self.smile_fw_version: Version | None = None
|
82
75
|
self.smile_hostname: str = NONE
|
83
76
|
self.smile_hw_version: str | None = None
|
84
77
|
self.smile_legacy = False
|
@@ -90,8 +83,40 @@ class Smile(SmileComm):
|
|
90
83
|
self.smile_version: Version | None = None
|
91
84
|
self.smile_zigbee_mac_address: str | None = None
|
92
85
|
|
86
|
+
@property
|
87
|
+
def cooling_present(self) -> bool:
|
88
|
+
"""Return the cooling capability."""
|
89
|
+
if "cooling_present" in self._smile_props:
|
90
|
+
return self._smile_props["cooling_present"]
|
91
|
+
return False
|
92
|
+
|
93
|
+
@property
|
94
|
+
def gateway_id(self) -> str:
|
95
|
+
"""Return the gateway-id."""
|
96
|
+
return self._smile_props["gateway_id"]
|
97
|
+
|
98
|
+
@property
|
99
|
+
def heater_id(self) -> str:
|
100
|
+
"""Return the heater-id."""
|
101
|
+
if "heater_id" in self._smile_props:
|
102
|
+
return self._smile_props["heater_id"]
|
103
|
+
return NONE
|
104
|
+
|
105
|
+
@property
|
106
|
+
def item_count(self) -> int:
|
107
|
+
"""Return the item-count."""
|
108
|
+
return self._smile_props["item_count"]
|
109
|
+
|
110
|
+
@property
|
111
|
+
def reboot(self) -> bool:
|
112
|
+
"""Return the reboot capability.
|
113
|
+
|
114
|
+
All non-legacy devices support gateway-rebooting.
|
115
|
+
"""
|
116
|
+
return not self.smile_legacy
|
117
|
+
|
93
118
|
async def connect(self) -> Version | None:
|
94
|
-
"""Connect to Plugwise
|
119
|
+
"""Connect to the Plugwise Gateway and determine its name, type, version, and other data."""
|
95
120
|
result = await self._request(DOMAIN_OBJECTS)
|
96
121
|
# Work-around for Stretch fw 2.7.18
|
97
122
|
if not (vendor_names := result.findall("./module/vendor_name")):
|
@@ -128,10 +153,6 @@ class Smile(SmileComm):
|
|
128
153
|
|
129
154
|
self._smile_api = (
|
130
155
|
SmileAPI(
|
131
|
-
self._host,
|
132
|
-
self._password,
|
133
|
-
self._request,
|
134
|
-
self._websession,
|
135
156
|
self._cooling_present,
|
136
157
|
self._elga,
|
137
158
|
self._is_thermostat,
|
@@ -139,9 +160,9 @@ class Smile(SmileComm):
|
|
139
160
|
self._loc_data,
|
140
161
|
self._on_off_device,
|
141
162
|
self._opentherm_device,
|
163
|
+
self._request,
|
142
164
|
self._schedule_old_states,
|
143
|
-
self.
|
144
|
-
self.smile_fw_version,
|
165
|
+
self._smile_props,
|
145
166
|
self.smile_hostname,
|
146
167
|
self.smile_hw_version,
|
147
168
|
self.smile_mac_address,
|
@@ -150,31 +171,25 @@ class Smile(SmileComm):
|
|
150
171
|
self.smile_name,
|
151
172
|
self.smile_type,
|
152
173
|
self.smile_version,
|
153
|
-
self._port,
|
154
|
-
self._username,
|
155
174
|
)
|
156
175
|
if not self.smile_legacy
|
157
176
|
else SmileLegacyAPI(
|
158
|
-
self._host,
|
159
|
-
self._password,
|
160
|
-
self._request,
|
161
|
-
self._websession,
|
162
177
|
self._is_thermostat,
|
163
178
|
self._loc_data,
|
164
179
|
self._on_off_device,
|
165
180
|
self._opentherm_device,
|
181
|
+
self._request,
|
182
|
+
self._smile_props,
|
166
183
|
self._stretch_v2,
|
167
184
|
self._target_smile,
|
168
|
-
self.smile_fw_version,
|
169
185
|
self.smile_hostname,
|
170
186
|
self.smile_hw_version,
|
171
187
|
self.smile_mac_address,
|
172
188
|
self.smile_model,
|
173
189
|
self.smile_name,
|
174
190
|
self.smile_type,
|
191
|
+
self.smile_version,
|
175
192
|
self.smile_zigbee_mac_address,
|
176
|
-
self._port,
|
177
|
-
self._username,
|
178
193
|
)
|
179
194
|
)
|
180
195
|
|
@@ -186,13 +201,13 @@ class Smile(SmileComm):
|
|
186
201
|
async def _smile_detect(self, result: etree, dsmrmain: etree) -> None:
|
187
202
|
"""Helper-function for connect().
|
188
203
|
|
189
|
-
Detect which type of
|
204
|
+
Detect which type of Plugwise Gateway is being connected.
|
190
205
|
"""
|
191
206
|
model: str = "Unknown"
|
192
207
|
if (gateway := result.find("./gateway")) is not None:
|
193
208
|
if (v_model := gateway.find("vendor_model")) is not None:
|
194
209
|
model = v_model.text
|
195
|
-
self.
|
210
|
+
self.smile_version = parse(gateway.find("firmware_version").text)
|
196
211
|
self.smile_hw_version = gateway.find("hardware_version").text
|
197
212
|
self.smile_hostname = gateway.find("hostname").text
|
198
213
|
self.smile_mac_address = gateway.find("mac_address").text
|
@@ -200,7 +215,7 @@ class Smile(SmileComm):
|
|
200
215
|
else:
|
201
216
|
model = await self._smile_detect_legacy(result, dsmrmain, model)
|
202
217
|
|
203
|
-
if model == "Unknown" or self.
|
218
|
+
if model == "Unknown" or self.smile_version is None: # pragma: no cover
|
204
219
|
# Corner case check
|
205
220
|
LOGGER.error(
|
206
221
|
"Unable to find model or version information, please create"
|
@@ -208,7 +223,7 @@ class Smile(SmileComm):
|
|
208
223
|
)
|
209
224
|
raise UnsupportedDeviceError
|
210
225
|
|
211
|
-
version_major = str(self.
|
226
|
+
version_major = str(self.smile_version.major)
|
212
227
|
self._target_smile = f"{model}_v{version_major}"
|
213
228
|
LOGGER.debug("Plugwise identified as %s", self._target_smile)
|
214
229
|
if self._target_smile not in SMILES:
|
@@ -232,7 +247,6 @@ class Smile(SmileComm):
|
|
232
247
|
self.smile_model = "Gateway"
|
233
248
|
self.smile_name = SMILES[self._target_smile].smile_name
|
234
249
|
self.smile_type = SMILES[self._target_smile].smile_type
|
235
|
-
self.smile_version = self.smile_fw_version
|
236
250
|
|
237
251
|
if self.smile_type == "stretch":
|
238
252
|
self._stretch_v2 = int(version_major) == 2
|
@@ -260,7 +274,10 @@ class Smile(SmileComm):
|
|
260
274
|
async def _smile_detect_legacy(
|
261
275
|
self, result: etree, dsmrmain: etree, model: str
|
262
276
|
) -> str:
|
263
|
-
"""Helper-function for _smile_detect().
|
277
|
+
"""Helper-function for _smile_detect().
|
278
|
+
|
279
|
+
Detect which type of legacy Plugwise Gateway is being connected.
|
280
|
+
"""
|
264
281
|
return_model = model
|
265
282
|
# Stretch: find the MAC of the zigbee master_controller (= Stick)
|
266
283
|
if (network := result.find("./module/protocols/master_controller")) is not None:
|
@@ -278,7 +295,7 @@ class Smile(SmileComm):
|
|
278
295
|
or network is not None
|
279
296
|
):
|
280
297
|
system = await self._request(SYSTEM)
|
281
|
-
self.
|
298
|
+
self.smile_version = parse(system.find("./gateway/firmware").text)
|
282
299
|
return_model = system.find("./gateway/product").text
|
283
300
|
self.smile_hostname = system.find("./gateway/hostname").text
|
284
301
|
# If wlan0 contains data it's active, so eth0 should be checked last
|
@@ -289,7 +306,7 @@ class Smile(SmileComm):
|
|
289
306
|
# P1 legacy:
|
290
307
|
elif dsmrmain is not None:
|
291
308
|
status = await self._request(STATUS)
|
292
|
-
self.
|
309
|
+
self.smile_version = parse(status.find("./system/version").text)
|
293
310
|
return_model = status.find("./system/product").text
|
294
311
|
self.smile_hostname = status.find("./network/hostname").text
|
295
312
|
self.smile_mac_address = status.find("./network/mac_address").text
|
@@ -304,20 +321,11 @@ class Smile(SmileComm):
|
|
304
321
|
self.smile_legacy = True
|
305
322
|
return return_model
|
306
323
|
|
307
|
-
async def
|
308
|
-
"""
|
309
|
-
|
310
|
-
|
311
|
-
def get_all_gateway_entities(self) -> None:
|
312
|
-
"""Helper-function used for testing."""
|
313
|
-
self._smile_api.get_all_gateway_entities()
|
314
|
-
|
315
|
-
async def async_update(self) -> PlugwiseData:
|
316
|
-
"""Update the various entities and their states."""
|
317
|
-
data = PlugwiseData(devices={}, gateway={})
|
324
|
+
async def async_update(self) -> dict[str, GwEntityData]:
|
325
|
+
"""Update the Plughwise Gateway entities and their data and states."""
|
326
|
+
data: dict[str, GwEntityData] = {}
|
318
327
|
try:
|
319
328
|
data = await self._smile_api.async_update()
|
320
|
-
self.gateway_id = data.gateway["gateway_id"]
|
321
329
|
except (DataMissingError, KeyError) as err:
|
322
330
|
raise PlugwiseError("No Plugwise data received") from err
|
323
331
|
|
plugwise/common.py
CHANGED
@@ -9,19 +9,17 @@ from typing import cast
|
|
9
9
|
|
10
10
|
from plugwise.constants import (
|
11
11
|
ANNA,
|
12
|
+
NONE,
|
12
13
|
SPECIAL_PLUG_TYPES,
|
13
14
|
SWITCH_GROUP_TYPES,
|
14
15
|
ApplianceType,
|
15
16
|
GwEntityData,
|
16
17
|
ModuleData,
|
17
|
-
SensorType,
|
18
18
|
)
|
19
19
|
from plugwise.util import (
|
20
|
-
check_alternative_location,
|
21
20
|
check_heater_central,
|
22
21
|
check_model,
|
23
22
|
get_vendor_name,
|
24
|
-
power_data_local_format,
|
25
23
|
return_valid,
|
26
24
|
)
|
27
25
|
|
@@ -29,19 +27,32 @@ from defusedxml import ElementTree as etree
|
|
29
27
|
from munch import Munch
|
30
28
|
|
31
29
|
|
30
|
+
def get_zigbee_data(module: etree, module_data: ModuleData, legacy: bool) -> None:
|
31
|
+
"""Helper-function for _get_module_data()."""
|
32
|
+
if legacy:
|
33
|
+
# Stretches
|
34
|
+
if (router := module.find("./protocols/network_router")) is not None:
|
35
|
+
module_data["zigbee_mac_address"] = router.find("mac_address").text
|
36
|
+
# Also look for the Circle+/Stealth M+
|
37
|
+
if (coord := module.find("./protocols/network_coordinator")) is not None:
|
38
|
+
module_data["zigbee_mac_address"] = coord.find("mac_address").text
|
39
|
+
# Adam
|
40
|
+
elif (zb_node := module.find("./protocols/zig_bee_node")) is not None:
|
41
|
+
module_data["zigbee_mac_address"] = zb_node.find("mac_address").text
|
42
|
+
module_data["reachable"] = zb_node.find("reachable").text == "true"
|
43
|
+
|
44
|
+
|
32
45
|
class SmileCommon:
|
33
46
|
"""The SmileCommon class."""
|
34
47
|
|
35
48
|
def __init__(self) -> None:
|
36
49
|
"""Init."""
|
37
|
-
self.
|
50
|
+
self._cooling_present: bool
|
38
51
|
self._count: int
|
39
52
|
self._domain_objects: etree
|
40
|
-
self.
|
41
|
-
self._heater_id: str
|
53
|
+
self._heater_id: str = NONE
|
42
54
|
self._on_off_device: bool
|
43
|
-
self.
|
44
|
-
self.gw_entities: dict[str, GwEntityData]
|
55
|
+
self.gw_entities: dict[str, GwEntityData] = {}
|
45
56
|
self.smile_name: str
|
46
57
|
self.smile_type: str
|
47
58
|
|
@@ -111,99 +122,6 @@ class SmileCommon:
|
|
111
122
|
|
112
123
|
return appl
|
113
124
|
|
114
|
-
def _collect_power_values(
|
115
|
-
self, data: GwEntityData, loc: Munch, tariff: str, legacy: bool = False
|
116
|
-
) -> None:
|
117
|
-
"""Something."""
|
118
|
-
for loc.peak_select in ("nl_peak", "nl_offpeak"):
|
119
|
-
loc.locator = (
|
120
|
-
f'./{loc.log_type}[type="{loc.measurement}"]/period/'
|
121
|
-
f'measurement[@{tariff}="{loc.peak_select}"]'
|
122
|
-
)
|
123
|
-
if legacy:
|
124
|
-
loc.locator = (
|
125
|
-
f"./{loc.meas_list[0]}_{loc.log_type}/measurement"
|
126
|
-
f'[@directionality="{loc.meas_list[1]}"][@{tariff}="{loc.peak_select}"]'
|
127
|
-
)
|
128
|
-
|
129
|
-
loc = self._power_data_peak_value(loc, legacy)
|
130
|
-
if not loc.found:
|
131
|
-
continue
|
132
|
-
|
133
|
-
data = self._power_data_energy_diff(
|
134
|
-
loc.measurement, loc.net_string, loc.f_val, data
|
135
|
-
)
|
136
|
-
key = cast(SensorType, loc.key_string)
|
137
|
-
data["sensors"][key] = loc.f_val
|
138
|
-
|
139
|
-
def _count_data_items(self, data: GwEntityData) -> None:
|
140
|
-
"""When present, count the binary_sensors, sensors and switches dict-items, don't count the dicts.
|
141
|
-
|
142
|
-
Also, count the remaining single data items, the amount of dicts present have already been pre-subtracted in the previous step.
|
143
|
-
"""
|
144
|
-
if "binary_sensors" in data:
|
145
|
-
self._count += len(data["binary_sensors"]) - 1
|
146
|
-
if "sensors" in data:
|
147
|
-
self._count += len(data["sensors"]) - 1
|
148
|
-
if "switches" in data:
|
149
|
-
self._count += len(data["switches"]) - 1
|
150
|
-
self._count += len(data)
|
151
|
-
|
152
|
-
def _power_data_peak_value(self, loc: Munch, legacy: bool) -> Munch:
|
153
|
-
"""Helper-function for _power_data_from_location() and _power_data_from_modules()."""
|
154
|
-
loc.found = True
|
155
|
-
if loc.logs.find(loc.locator) is None:
|
156
|
-
loc = check_alternative_location(loc, legacy)
|
157
|
-
if not loc.found:
|
158
|
-
return loc
|
159
|
-
|
160
|
-
if (peak := loc.peak_select.split("_")[1]) == "offpeak":
|
161
|
-
peak = "off_peak"
|
162
|
-
log_found = loc.log_type.split("_")[0]
|
163
|
-
loc.key_string = f"{loc.measurement}_{peak}_{log_found}"
|
164
|
-
if "gas" in loc.measurement or loc.log_type == "point_meter":
|
165
|
-
loc.key_string = f"{loc.measurement}_{log_found}"
|
166
|
-
# Only for P1 Actual -------------------#
|
167
|
-
if "phase" in loc.measurement:
|
168
|
-
loc.key_string = f"{loc.measurement}"
|
169
|
-
# --------------------------------------#
|
170
|
-
loc.net_string = f"net_electricity_{log_found}"
|
171
|
-
val = loc.logs.find(loc.locator).text
|
172
|
-
loc.f_val = power_data_local_format(loc.attrs, loc.key_string, val)
|
173
|
-
|
174
|
-
return loc
|
175
|
-
|
176
|
-
def _power_data_energy_diff(
|
177
|
-
self,
|
178
|
-
measurement: str,
|
179
|
-
net_string: SensorType,
|
180
|
-
f_val: float | int,
|
181
|
-
data: GwEntityData,
|
182
|
-
) -> GwEntityData:
|
183
|
-
"""Calculate differential energy."""
|
184
|
-
if (
|
185
|
-
"electricity" in measurement
|
186
|
-
and "phase" not in measurement
|
187
|
-
and "interval" not in net_string
|
188
|
-
):
|
189
|
-
diff = 1
|
190
|
-
if "produced" in measurement:
|
191
|
-
diff = -1
|
192
|
-
if net_string not in data["sensors"]:
|
193
|
-
tmp_val: float | int = 0
|
194
|
-
else:
|
195
|
-
tmp_val = data["sensors"][net_string]
|
196
|
-
|
197
|
-
if isinstance(f_val, int):
|
198
|
-
tmp_val += f_val * diff
|
199
|
-
else:
|
200
|
-
tmp_val += float(f_val * diff)
|
201
|
-
tmp_val = float(f"{round(tmp_val, 3):.3f}")
|
202
|
-
|
203
|
-
data["sensors"][net_string] = tmp_val
|
204
|
-
|
205
|
-
return data
|
206
|
-
|
207
125
|
def _create_gw_entities(self, appl: Munch) -> None:
|
208
126
|
"""Helper-function for creating/updating gw_entities."""
|
209
127
|
self.gw_entities[appl.entity_id] = {"dev_class": appl.pwclass}
|
@@ -324,22 +242,6 @@ class SmileCommon:
|
|
324
242
|
module_data["vendor_model"] = module.find("vendor_model").text
|
325
243
|
module_data["hardware_version"] = module.find("hardware_version").text
|
326
244
|
module_data["firmware_version"] = module.find("firmware_version").text
|
327
|
-
|
245
|
+
get_zigbee_data(module, module_data, legacy)
|
328
246
|
|
329
247
|
return module_data
|
330
|
-
|
331
|
-
def _get_zigbee_data(
|
332
|
-
self, module: etree, module_data: ModuleData, legacy: bool
|
333
|
-
) -> None:
|
334
|
-
"""Helper-function for _get_module_data()."""
|
335
|
-
if legacy:
|
336
|
-
# Stretches
|
337
|
-
if (router := module.find("./protocols/network_router")) is not None:
|
338
|
-
module_data["zigbee_mac_address"] = router.find("mac_address").text
|
339
|
-
# Also look for the Circle+/Stealth M+
|
340
|
-
if (coord := module.find("./protocols/network_coordinator")) is not None:
|
341
|
-
module_data["zigbee_mac_address"] = coord.find("mac_address").text
|
342
|
-
# Adam
|
343
|
-
elif (zb_node := module.find("./protocols/zig_bee_node")) is not None:
|
344
|
-
module_data["zigbee_mac_address"] = zb_node.find("mac_address").text
|
345
|
-
module_data["reachable"] = zb_node.find("reachable").text == "true"
|
plugwise/constants.py
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
from collections import namedtuple
|
6
|
-
from dataclasses import dataclass
|
7
6
|
import logging
|
8
7
|
from typing import Final, Literal, TypedDict, get_args
|
9
8
|
|
@@ -394,14 +393,13 @@ ZONE_THERMOSTATS: Final[tuple[str, ...]] = (
|
|
394
393
|
)
|
395
394
|
|
396
395
|
|
397
|
-
class
|
398
|
-
"""The
|
396
|
+
class SmileProps(TypedDict, total=False):
|
397
|
+
"""The SmileProps Data class."""
|
399
398
|
|
400
399
|
cooling_present: bool
|
401
400
|
gateway_id: str
|
402
401
|
heater_id: str
|
403
402
|
item_count: int
|
404
|
-
notifications: dict[str, dict[str, str]]
|
405
403
|
reboot: bool
|
406
404
|
smile_name: str
|
407
405
|
|
@@ -552,6 +550,7 @@ class GwEntityData(TypedDict, total=False):
|
|
552
550
|
|
553
551
|
# Gateway
|
554
552
|
gateway_modes: list[str]
|
553
|
+
notifications: dict[str, dict[str, str]]
|
555
554
|
regulation_modes: list[str]
|
556
555
|
select_gateway_mode: str
|
557
556
|
select_regulation_mode: str
|
@@ -577,11 +576,3 @@ class GwEntityData(TypedDict, total=False):
|
|
577
576
|
switches: SmileSwitches
|
578
577
|
temperature_offset: ActuatorData
|
579
578
|
thermostat: ActuatorData
|
580
|
-
|
581
|
-
|
582
|
-
@dataclass
|
583
|
-
class PlugwiseData:
|
584
|
-
"""Plugwise data provided as output."""
|
585
|
-
|
586
|
-
devices: dict[str, GwEntityData]
|
587
|
-
gateway: GatewayData
|
plugwise/data.py
CHANGED
@@ -16,6 +16,7 @@ from plugwise.constants import (
|
|
16
16
|
OFF,
|
17
17
|
ActuatorData,
|
18
18
|
GwEntityData,
|
19
|
+
SmileProps,
|
19
20
|
)
|
20
21
|
from plugwise.helper import SmileHelper
|
21
22
|
from plugwise.util import remove_empty_platform_dicts
|
@@ -26,31 +27,27 @@ class SmileData(SmileHelper):
|
|
26
27
|
|
27
28
|
def __init__(self) -> None:
|
28
29
|
"""Init."""
|
30
|
+
self._smile_props: SmileProps
|
31
|
+
self._zones: dict[str, GwEntityData] = {}
|
29
32
|
SmileHelper.__init__(self)
|
30
33
|
|
31
34
|
def _all_entity_data(self) -> None:
|
32
35
|
"""Helper-function for get_all_gateway_entities().
|
33
36
|
|
34
|
-
Collect data for each entity and add to self.
|
37
|
+
Collect data for each entity and add to self._smile_props and self.gw_entities.
|
35
38
|
"""
|
36
39
|
self._update_gw_entities()
|
37
40
|
if self.smile(ADAM):
|
38
41
|
self._update_zones()
|
39
42
|
self.gw_entities.update(self._zones)
|
40
43
|
|
41
|
-
self.
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
"notifications": self._notifications,
|
46
|
-
"reboot": True,
|
47
|
-
"smile_name": self.smile_name,
|
48
|
-
}
|
49
|
-
)
|
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
|
50
48
|
if self._is_thermostat:
|
51
|
-
self.
|
52
|
-
|
53
|
-
)
|
49
|
+
self._smile_props["heater_id"] = self._heater_id
|
50
|
+
self._smile_props["cooling_present"] = self._cooling_present
|
54
51
|
|
55
52
|
def _update_zones(self) -> None:
|
56
53
|
"""Helper-function for _all_entity_data() and async_update().
|
@@ -69,7 +66,7 @@ class SmileData(SmileHelper):
|
|
69
66
|
mac_list: list[str] = []
|
70
67
|
for entity_id, entity in self.gw_entities.items():
|
71
68
|
data = self._get_entity_data(entity_id)
|
72
|
-
if entity_id == self.
|
69
|
+
if entity_id == self._gateway_id:
|
73
70
|
mac_list = self._detect_low_batteries()
|
74
71
|
self._add_or_update_notifications(entity_id, entity, data)
|
75
72
|
|
@@ -123,14 +120,15 @@ class SmileData(SmileHelper):
|
|
123
120
|
) -> None:
|
124
121
|
"""Helper-function adding or updating the Plugwise notifications."""
|
125
122
|
if (
|
126
|
-
entity_id == self.
|
123
|
+
entity_id == self._gateway_id
|
127
124
|
and (self._is_thermostat or self.smile_type == "power")
|
128
125
|
) or (
|
129
126
|
"binary_sensors" in entity
|
130
127
|
and "plugwise_notification" in entity["binary_sensors"]
|
131
128
|
):
|
132
129
|
data["binary_sensors"]["plugwise_notification"] = bool(self._notifications)
|
133
|
-
self.
|
130
|
+
data["notifications"] = self._notifications
|
131
|
+
self._count += 2
|
134
132
|
|
135
133
|
def _update_for_cooling(self, entity: GwEntityData) -> None:
|
136
134
|
"""Helper-function for adding/updating various cooling-related values."""
|
@@ -305,7 +303,7 @@ class SmileData(SmileHelper):
|
|
305
303
|
|
306
304
|
def check_reg_mode(self, mode: str) -> bool:
|
307
305
|
"""Helper-function for device_data_climate()."""
|
308
|
-
gateway = self.gw_entities[self.
|
306
|
+
gateway = self.gw_entities[self._gateway_id]
|
309
307
|
return (
|
310
308
|
"regulation_modes" in gateway and gateway["select_regulation_mode"] == mode
|
311
309
|
)
|
@@ -313,6 +311,7 @@ class SmileData(SmileHelper):
|
|
313
311
|
def _get_anna_control_state(self, data: GwEntityData) -> None:
|
314
312
|
"""Set the thermostat control_state based on the opentherm/onoff device state."""
|
315
313
|
data["control_state"] = "idle"
|
314
|
+
self._count += 1
|
316
315
|
for entity in self.gw_entities.values():
|
317
316
|
if entity["dev_class"] != "heater_central":
|
318
317
|
continue
|