plugwise 0.37.1a2__py3-none-any.whl → 0.37.2__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/common.py +194 -16
- plugwise/constants.py +47 -44
- plugwise/data.py +57 -73
- plugwise/helper.py +430 -614
- plugwise/legacy/data.py +28 -44
- plugwise/legacy/helper.py +174 -357
- plugwise/legacy/smile.py +42 -42
- plugwise/smile.py +130 -130
- plugwise/util.py +62 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.2.dist-info}/METADATA +1 -4
- plugwise-0.37.2.dist-info/RECORD +17 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.2.dist-info}/WHEEL +1 -1
- plugwise-0.37.1a2.dist-info/RECORD +0 -17
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.2.dist-info}/LICENSE +0 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.2.dist-info}/top_level.txt +0 -0
plugwise/common.py
CHANGED
@@ -4,11 +4,23 @@ Plugwise Smile protocol helpers.
|
|
4
4
|
"""
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
from
|
7
|
+
from typing import cast
|
8
|
+
|
9
|
+
from plugwise.constants import (
|
10
|
+
ANNA,
|
11
|
+
SPECIAL_PLUG_TYPES,
|
12
|
+
SWITCH_GROUP_TYPES,
|
13
|
+
ApplianceType,
|
14
|
+
DeviceData,
|
15
|
+
ModelData,
|
16
|
+
SensorType,
|
17
|
+
)
|
8
18
|
from plugwise.util import (
|
19
|
+
check_alternative_location,
|
9
20
|
check_heater_central,
|
10
21
|
check_model,
|
11
22
|
get_vendor_name,
|
23
|
+
power_data_local_format,
|
12
24
|
return_valid,
|
13
25
|
)
|
14
26
|
|
@@ -22,31 +34,20 @@ class SmileCommon:
|
|
22
34
|
def __init__(self) -> None:
|
23
35
|
"""Init."""
|
24
36
|
self._appliances: etree
|
37
|
+
self._count: int
|
25
38
|
self._domain_objects: etree
|
26
39
|
self._cooling_present: bool
|
27
40
|
self._heater_id: str
|
28
41
|
self._on_off_device: bool
|
29
42
|
self._opentherm_device: bool
|
43
|
+
self.gw_devices: dict[str, DeviceData]
|
30
44
|
self.smile_name: str
|
45
|
+
self.smile_type: str
|
31
46
|
|
32
47
|
def smile(self, name: str) -> bool:
|
33
48
|
"""Helper-function checking the smile-name."""
|
34
49
|
return self.smile_name == name
|
35
50
|
|
36
|
-
def _appl_thermostat_info(self, appl: Munch, xml_1: etree, xml_2: etree = None) -> Munch:
|
37
|
-
"""Helper-function for _appliance_info_finder()."""
|
38
|
-
locator = "./logs/point_log[type='thermostat']/thermostat"
|
39
|
-
mod_type = "thermostat"
|
40
|
-
xml_2 = return_valid(xml_2, self._domain_objects)
|
41
|
-
module_data = self._get_module_data(xml_1, locator, mod_type, xml_2)
|
42
|
-
appl.vendor_name = module_data["vendor_name"]
|
43
|
-
appl.model = check_model(module_data["vendor_model"], appl.vendor_name)
|
44
|
-
appl.hardware = module_data["hardware_version"]
|
45
|
-
appl.firmware = module_data["firmware_version"]
|
46
|
-
appl.zigbee_mac = module_data["zigbee_mac_address"]
|
47
|
-
|
48
|
-
return appl
|
49
|
-
|
50
51
|
def _appl_heater_central_info(
|
51
52
|
self,
|
52
53
|
appl: Munch,
|
@@ -94,6 +95,184 @@ class SmileCommon:
|
|
94
95
|
|
95
96
|
return appl
|
96
97
|
|
98
|
+
def _appl_thermostat_info(self, appl: Munch, xml_1: etree, xml_2: etree = None) -> Munch:
|
99
|
+
"""Helper-function for _appliance_info_finder()."""
|
100
|
+
locator = "./logs/point_log[type='thermostat']/thermostat"
|
101
|
+
mod_type = "thermostat"
|
102
|
+
xml_2 = return_valid(xml_2, self._domain_objects)
|
103
|
+
module_data = self._get_module_data(xml_1, locator, mod_type, xml_2)
|
104
|
+
appl.vendor_name = module_data["vendor_name"]
|
105
|
+
appl.model = check_model(module_data["vendor_model"], appl.vendor_name)
|
106
|
+
appl.hardware = module_data["hardware_version"]
|
107
|
+
appl.firmware = module_data["firmware_version"]
|
108
|
+
appl.zigbee_mac = module_data["zigbee_mac_address"]
|
109
|
+
|
110
|
+
return appl
|
111
|
+
|
112
|
+
def _collect_power_values(self, data: DeviceData, loc: Munch, tariff: str, legacy: bool = False) -> None:
|
113
|
+
"""Something."""
|
114
|
+
for loc.peak_select in ("nl_peak", "nl_offpeak"):
|
115
|
+
loc.locator = (
|
116
|
+
f'./{loc.log_type}[type="{loc.measurement}"]/period/'
|
117
|
+
f'measurement[@{tariff}="{loc.peak_select}"]'
|
118
|
+
)
|
119
|
+
if legacy:
|
120
|
+
loc.locator = (
|
121
|
+
f"./{loc.meas_list[0]}_{loc.log_type}/measurement"
|
122
|
+
f'[@directionality="{loc.meas_list[1]}"][@{tariff}="{loc.peak_select}"]'
|
123
|
+
)
|
124
|
+
|
125
|
+
loc = self._power_data_peak_value(loc, legacy)
|
126
|
+
if not loc.found:
|
127
|
+
continue
|
128
|
+
|
129
|
+
data = self._power_data_energy_diff(
|
130
|
+
loc.measurement, loc.net_string, loc.f_val, data
|
131
|
+
)
|
132
|
+
key = cast(SensorType, loc.key_string)
|
133
|
+
data["sensors"][key] = loc.f_val
|
134
|
+
|
135
|
+
def _power_data_peak_value(self, loc: Munch, legacy: bool) -> Munch:
|
136
|
+
"""Helper-function for _power_data_from_location() and _power_data_from_modules()."""
|
137
|
+
loc.found = True
|
138
|
+
if loc.logs.find(loc.locator) is None:
|
139
|
+
loc = check_alternative_location(loc, legacy)
|
140
|
+
if not loc.found:
|
141
|
+
return loc
|
142
|
+
|
143
|
+
if (peak := loc.peak_select.split("_")[1]) == "offpeak":
|
144
|
+
peak = "off_peak"
|
145
|
+
log_found = loc.log_type.split("_")[0]
|
146
|
+
loc.key_string = f"{loc.measurement}_{peak}_{log_found}"
|
147
|
+
if "gas" in loc.measurement or loc.log_type == "point_meter":
|
148
|
+
loc.key_string = f"{loc.measurement}_{log_found}"
|
149
|
+
# Only for P1 Actual -------------------#
|
150
|
+
if "phase" in loc.measurement:
|
151
|
+
loc.key_string = f"{loc.measurement}"
|
152
|
+
# --------------------------------------#
|
153
|
+
loc.net_string = f"net_electricity_{log_found}"
|
154
|
+
val = loc.logs.find(loc.locator).text
|
155
|
+
loc.f_val = power_data_local_format(loc.attrs, loc.key_string, val)
|
156
|
+
|
157
|
+
return loc
|
158
|
+
|
159
|
+
def _power_data_energy_diff(
|
160
|
+
self,
|
161
|
+
measurement: str,
|
162
|
+
net_string: SensorType,
|
163
|
+
f_val: float | int,
|
164
|
+
direct_data: DeviceData,
|
165
|
+
) -> DeviceData:
|
166
|
+
"""Calculate differential energy."""
|
167
|
+
if (
|
168
|
+
"electricity" in measurement
|
169
|
+
and "phase" not in measurement
|
170
|
+
and "interval" not in net_string
|
171
|
+
):
|
172
|
+
diff = 1
|
173
|
+
if "produced" in measurement:
|
174
|
+
diff = -1
|
175
|
+
if net_string not in direct_data["sensors"]:
|
176
|
+
tmp_val: float | int = 0
|
177
|
+
else:
|
178
|
+
tmp_val = direct_data["sensors"][net_string]
|
179
|
+
|
180
|
+
if isinstance(f_val, int):
|
181
|
+
tmp_val += f_val * diff
|
182
|
+
else:
|
183
|
+
tmp_val += float(f_val * diff)
|
184
|
+
tmp_val = float(f"{round(tmp_val, 3):.3f}")
|
185
|
+
|
186
|
+
direct_data["sensors"][net_string] = tmp_val
|
187
|
+
|
188
|
+
return direct_data
|
189
|
+
|
190
|
+
def _create_gw_devices(self, appl: Munch) -> None:
|
191
|
+
"""Helper-function for creating/updating gw_devices."""
|
192
|
+
self.gw_devices[appl.dev_id] = {"dev_class": appl.pwclass}
|
193
|
+
self._count += 1
|
194
|
+
for key, value in {
|
195
|
+
"firmware": appl.firmware,
|
196
|
+
"hardware": appl.hardware,
|
197
|
+
"location": appl.location,
|
198
|
+
"mac_address": appl.mac,
|
199
|
+
"model": appl.model,
|
200
|
+
"name": appl.name,
|
201
|
+
"zigbee_mac_address": appl.zigbee_mac,
|
202
|
+
"vendor": appl.vendor_name,
|
203
|
+
}.items():
|
204
|
+
if value is not None or key == "location":
|
205
|
+
appl_key = cast(ApplianceType, key)
|
206
|
+
self.gw_devices[appl.dev_id][appl_key] = value
|
207
|
+
self._count += 1
|
208
|
+
|
209
|
+
def _device_data_switching_group(
|
210
|
+
self, device: DeviceData, data: DeviceData
|
211
|
+
) -> None:
|
212
|
+
"""Helper-function for _get_device_data().
|
213
|
+
|
214
|
+
Determine switching group device data.
|
215
|
+
"""
|
216
|
+
if device["dev_class"] in SWITCH_GROUP_TYPES:
|
217
|
+
counter = 0
|
218
|
+
for member in device["members"]:
|
219
|
+
if self.gw_devices[member]["switches"].get("relay"):
|
220
|
+
counter += 1
|
221
|
+
data["switches"]["relay"] = counter != 0
|
222
|
+
self._count += 1
|
223
|
+
|
224
|
+
def _get_group_switches(self) -> dict[str, DeviceData]:
|
225
|
+
"""Helper-function for smile.py: get_all_devices().
|
226
|
+
|
227
|
+
Collect switching- or pump-group info.
|
228
|
+
"""
|
229
|
+
switch_groups: dict[str, DeviceData] = {}
|
230
|
+
# P1 and Anna don't have switchgroups
|
231
|
+
if self.smile_type == "power" or self.smile(ANNA):
|
232
|
+
return switch_groups
|
233
|
+
|
234
|
+
for group in self._domain_objects.findall("./group"):
|
235
|
+
members: list[str] = []
|
236
|
+
group_id = group.attrib["id"]
|
237
|
+
group_name = group.find("name").text
|
238
|
+
group_type = group.find("type").text
|
239
|
+
group_appliances = group.findall("appliances/appliance")
|
240
|
+
for item in group_appliances:
|
241
|
+
# Check if members are not orphaned - stretch
|
242
|
+
if item.attrib["id"] in self.gw_devices:
|
243
|
+
members.append(item.attrib["id"])
|
244
|
+
|
245
|
+
if group_type in SWITCH_GROUP_TYPES and members:
|
246
|
+
switch_groups.update(
|
247
|
+
{
|
248
|
+
group_id: {
|
249
|
+
"dev_class": group_type,
|
250
|
+
"model": "Switchgroup",
|
251
|
+
"name": group_name,
|
252
|
+
"members": members,
|
253
|
+
},
|
254
|
+
},
|
255
|
+
)
|
256
|
+
self._count += 4
|
257
|
+
|
258
|
+
return switch_groups
|
259
|
+
|
260
|
+
def _get_lock_state(self, xml: etree, data: DeviceData, stretch_v2: bool = False) -> None:
|
261
|
+
"""Helper-function for _get_measurement_data().
|
262
|
+
|
263
|
+
Adam & Stretches: obtain the relay-switch lock state.
|
264
|
+
"""
|
265
|
+
actuator = "actuator_functionalities"
|
266
|
+
func_type = "relay_functionality"
|
267
|
+
if stretch_v2:
|
268
|
+
actuator = "actuators"
|
269
|
+
func_type = "relay"
|
270
|
+
if xml.find("type").text not in SPECIAL_PLUG_TYPES:
|
271
|
+
locator = f"./{actuator}/{func_type}/lock"
|
272
|
+
if (found := xml.find(locator)) is not None:
|
273
|
+
data["switches"]["lock"] = found.text == "true"
|
274
|
+
self._count += 1
|
275
|
+
|
97
276
|
def _get_module_data(
|
98
277
|
self,
|
99
278
|
xml_1: etree,
|
@@ -148,4 +327,3 @@ class SmileCommon:
|
|
148
327
|
elif (zb_node := module.find("./protocols/zig_bee_node")) is not None:
|
149
328
|
model_data["zigbee_mac_address"] = zb_node.find("mac_address").text
|
150
329
|
model_data["reachable"] = zb_node.find("reachable").text == "true"
|
151
|
-
|
plugwise/constants.py
CHANGED
@@ -121,21 +121,20 @@ P1_LEGACY_MEASUREMENTS: Final[dict[str, UOM]] = {
|
|
121
121
|
# radiator_valve: 'uncorrected_temperature', 'temperature_offset'
|
122
122
|
|
123
123
|
DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
|
124
|
-
#
|
125
|
-
"
|
126
|
-
# HA Core thermostat
|
127
|
-
"thermostat": DATA("setpoint", TEMP_CELSIUS),
|
128
|
-
|
129
|
-
"illuminance": UOM(UNIT_LUMEN),
|
124
|
+
"humidity": UOM(PERCENTAGE), # Specific for a Jip
|
125
|
+
"illuminance": UOM(UNIT_LUMEN), # Specific for an Anna
|
126
|
+
"temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature
|
127
|
+
"thermostat": DATA("setpoint", TEMP_CELSIUS), # HA Core thermostat setpoint
|
128
|
+
########################################################
|
130
129
|
# Specific for an Anna with heatpump extension installed
|
131
130
|
"cooling_activation_outdoor_temperature": UOM(TEMP_CELSIUS),
|
132
131
|
"cooling_deactivation_threshold": UOM(TEMP_CELSIUS),
|
133
|
-
|
132
|
+
##################################
|
133
|
+
# Specific for a Lisa or Tom/Floor
|
134
134
|
"battery": UOM(PERCENTAGE),
|
135
135
|
"temperature_difference": UOM(DEGREE),
|
136
136
|
"valve_position": UOM(PERCENTAGE),
|
137
|
-
|
138
|
-
"humidity": UOM(PERCENTAGE),
|
137
|
+
#####################
|
139
138
|
# Specific for a Plug
|
140
139
|
"electricity_consumed": UOM(POWER_WATT),
|
141
140
|
"electricity_produced": UOM(POWER_WATT),
|
@@ -144,39 +143,43 @@ DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
|
|
144
143
|
|
145
144
|
# Heater Central related measurements
|
146
145
|
HEATER_CENTRAL_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
|
146
|
+
"boiler_state": DATA(
|
147
|
+
"flame_state", NONE
|
148
|
+
), # Legacy Anna: similar to flame-state on Anna/Adam
|
147
149
|
"boiler_temperature": DATA("water_temperature", TEMP_CELSIUS),
|
150
|
+
"central_heating_state": DATA(
|
151
|
+
"c_heating_state", NONE
|
152
|
+
), # For Elga (heatpump) use this instead of intended_central_heating_state
|
153
|
+
"central_heater_water_pressure": DATA("water_pressure", PRESSURE_BAR),
|
154
|
+
"compressor_state": UOM(NONE), # present with heatpump
|
155
|
+
"cooling_enabled": UOM(
|
156
|
+
NONE
|
157
|
+
), # Available with the Loria and Elga (newer Anna firmware) heatpumps
|
158
|
+
"cooling_state": UOM(NONE),
|
148
159
|
"domestic_hot_water_mode": DATA("select_dhw_mode", NONE),
|
149
160
|
"domestic_hot_water_setpoint": UOM(TEMP_CELSIUS),
|
150
161
|
"domestic_hot_water_state": DATA("dhw_state", NONE),
|
151
162
|
"domestic_hot_water_temperature": DATA("dhw_temperature", TEMP_CELSIUS),
|
152
163
|
"elga_status_code": UOM(NONE),
|
164
|
+
"intended_boiler_state": DATA(
|
165
|
+
"heating_state", NONE
|
166
|
+
), # Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably
|
167
|
+
"flame_state": UOM(
|
168
|
+
NONE
|
169
|
+
), # Also present when there is a single gas-heater
|
153
170
|
"intended_boiler_temperature": UOM(
|
154
171
|
TEMP_CELSIUS
|
155
172
|
), # Non-zero when heating, zero when dhw-heating
|
156
|
-
"central_heating_state": DATA(
|
157
|
-
"c_heating_state", NONE
|
158
|
-
), # For Elga (heatpump) use this instead of intended_central_heating_state
|
159
173
|
"intended_central_heating_state": DATA(
|
160
174
|
"heating_state", NONE
|
161
175
|
), # This key shows in general the heating-behavior better than c-h_state. except when connected to a heatpump
|
162
176
|
"modulation_level": UOM(PERCENTAGE),
|
163
177
|
"return_water_temperature": DATA("return_temperature", TEMP_CELSIUS),
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
"
|
168
|
-
#
|
169
|
-
"cooling_enabled": UOM(NONE),
|
170
|
-
# Next 2 keys are used to show the state of the gas-heater used next to the Elga heatpump - marcelveldt
|
171
|
-
"slave_boiler_state": UOM(NONE),
|
172
|
-
"flame_state": UOM(NONE), # Also present when there is a single gas-heater
|
173
|
-
"central_heater_water_pressure": DATA("water_pressure", PRESSURE_BAR),
|
174
|
-
# Legacy Anna: similar to flame-state on Anna/Adam
|
175
|
-
"boiler_state": DATA("flame_state", NONE),
|
176
|
-
# Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably
|
177
|
-
"intended_boiler_state": DATA("heating_state", NONE),
|
178
|
-
# Outdoor temperature from APPLIANCES - present for a heatpump
|
179
|
-
"outdoor_temperature": DATA("outdoor_air_temperature", TEMP_CELSIUS),
|
178
|
+
"outdoor_temperature": DATA(
|
179
|
+
"outdoor_air_temperature", TEMP_CELSIUS
|
180
|
+
), # Outdoor temperature from APPLIANCES - present for a heatpump
|
181
|
+
"slave_boiler_state": DATA("secondary_boiler_state", NONE),
|
182
|
+
"thermostat_supports_cooling": UOM(NONE), # present with heatpump
|
180
183
|
}
|
181
184
|
|
182
185
|
OBSOLETE_MEASUREMENTS: Final[tuple[str, ...]] = (
|
@@ -253,22 +256,22 @@ ApplianceType = Literal[
|
|
253
256
|
]
|
254
257
|
|
255
258
|
BinarySensorType = Literal[
|
256
|
-
"cooling_enabled",
|
257
259
|
"compressor_state",
|
260
|
+
"cooling_enabled",
|
258
261
|
"cooling_state",
|
259
262
|
"dhw_state",
|
260
263
|
"flame_state",
|
261
264
|
"heating_state",
|
262
265
|
"plugwise_notification",
|
263
|
-
"
|
266
|
+
"secondary_boiler_state",
|
264
267
|
]
|
265
268
|
BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType)
|
266
269
|
|
267
270
|
LIMITS: Final[tuple[str, ...]] = (
|
271
|
+
"lower_bound",
|
268
272
|
"offset",
|
269
|
-
"setpoint",
|
270
273
|
"resolution",
|
271
|
-
"
|
274
|
+
"setpoint",
|
272
275
|
"upper_bound",
|
273
276
|
)
|
274
277
|
|
@@ -329,8 +332,8 @@ SENSORS: Final[tuple[str, ...]] = get_args(SensorType)
|
|
329
332
|
|
330
333
|
SPECIAL_PLUG_TYPES: Final[tuple[str, ...]] = (
|
331
334
|
"central_heating_pump",
|
332
|
-
"valve_actuator",
|
333
335
|
"heater_electric",
|
336
|
+
"valve_actuator",
|
334
337
|
)
|
335
338
|
|
336
339
|
SpecialType = Literal[
|
@@ -350,14 +353,14 @@ SwitchType = Literal[
|
|
350
353
|
]
|
351
354
|
SWITCHES: Final[tuple[str, ...]] = get_args(SwitchType)
|
352
355
|
|
353
|
-
SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("
|
356
|
+
SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("report", "switching")
|
354
357
|
|
355
358
|
THERMOSTAT_CLASSES: Final[tuple[str, ...]] = (
|
356
359
|
"thermostat",
|
360
|
+
"thermostatic_radiator_valve",
|
357
361
|
"thermo_sensor",
|
358
362
|
"zone_thermometer",
|
359
363
|
"zone_thermostat",
|
360
|
-
"thermostatic_radiator_valve",
|
361
364
|
)
|
362
365
|
|
363
366
|
ToggleNameType = Literal[
|
@@ -392,25 +395,25 @@ class ModelData(TypedDict):
|
|
392
395
|
"""The ModelData class."""
|
393
396
|
|
394
397
|
contents: bool
|
395
|
-
vendor_name: str | None
|
396
|
-
vendor_model: str | None
|
397
|
-
hardware_version: str | None
|
398
398
|
firmware_version: str | None
|
399
|
-
|
399
|
+
hardware_version: str | None
|
400
400
|
reachable: bool | None
|
401
|
+
vendor_model: str | None
|
402
|
+
vendor_name: str | None
|
403
|
+
zigbee_mac_address: str | None
|
401
404
|
|
402
405
|
|
403
406
|
class SmileBinarySensors(TypedDict, total=False):
|
404
407
|
"""Smile Binary Sensors class."""
|
405
408
|
|
406
|
-
cooling_enabled: bool
|
407
409
|
compressor_state: bool
|
410
|
+
cooling_enabled: bool
|
408
411
|
cooling_state: bool
|
409
412
|
dhw_state: bool
|
410
413
|
flame_state: bool
|
411
414
|
heating_state: bool
|
412
415
|
plugwise_notification: bool
|
413
|
-
|
416
|
+
secondary_boiler_state: bool
|
414
417
|
|
415
418
|
|
416
419
|
class SmileSensors(TypedDict, total=False):
|
@@ -482,9 +485,9 @@ class ThermoLoc(TypedDict, total=False):
|
|
482
485
|
"""Thermo Location class."""
|
483
486
|
|
484
487
|
name: str
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
+
primary: str | None
|
489
|
+
primary_prio: int
|
490
|
+
secondary: set[str]
|
488
491
|
|
489
492
|
|
490
493
|
class ActuatorData(TypedDict, total=False):
|
plugwise/data.py
CHANGED
@@ -11,7 +11,6 @@ from plugwise.constants import (
|
|
11
11
|
MIN_SETPOINT,
|
12
12
|
NONE,
|
13
13
|
OFF,
|
14
|
-
SWITCH_GROUP_TYPES,
|
15
14
|
ZONE_THERMOSTATS,
|
16
15
|
ActuatorData,
|
17
16
|
DeviceData,
|
@@ -28,6 +27,25 @@ class SmileData(SmileHelper):
|
|
28
27
|
SmileHelper.__init__(self)
|
29
28
|
|
30
29
|
|
30
|
+
def _all_device_data(self) -> None:
|
31
|
+
"""Helper-function for get_all_devices().
|
32
|
+
|
33
|
+
Collect data for each device and add to self.gw_data and self.gw_devices.
|
34
|
+
"""
|
35
|
+
self._update_gw_devices()
|
36
|
+
self.gw_data.update(
|
37
|
+
{
|
38
|
+
"gateway_id": self.gateway_id,
|
39
|
+
"item_count": self._count,
|
40
|
+
"notifications": self._notifications,
|
41
|
+
"smile_name": self.smile_name,
|
42
|
+
}
|
43
|
+
)
|
44
|
+
if self._is_thermostat:
|
45
|
+
self.gw_data.update(
|
46
|
+
{"heater_id": self._heater_id, "cooling_present": self._cooling_present}
|
47
|
+
)
|
48
|
+
|
31
49
|
def _update_gw_devices(self) -> None:
|
32
50
|
"""Helper-function for _all_device_data() and async_update().
|
33
51
|
|
@@ -84,39 +102,52 @@ class SmileData(SmileHelper):
|
|
84
102
|
sensors["setpoint_high"] = temp_dict["setpoint_high"]
|
85
103
|
self._count += 2
|
86
104
|
|
87
|
-
def
|
88
|
-
"""Helper-function for
|
105
|
+
def _get_device_data(self, dev_id: str) -> DeviceData:
|
106
|
+
"""Helper-function for _all_device_data() and async_update().
|
89
107
|
|
90
|
-
|
108
|
+
Provide device-data, based on Location ID (= dev_id), from APPLIANCES.
|
91
109
|
"""
|
92
|
-
self.
|
93
|
-
self.
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}
|
110
|
+
device = self.gw_devices[dev_id]
|
111
|
+
data = self._get_measurement_data(dev_id)
|
112
|
+
|
113
|
+
# Check availability of wired-connected devices
|
114
|
+
# Smartmeter
|
115
|
+
self._check_availability(
|
116
|
+
device, "smartmeter", data, "P1 does not seem to be connected"
|
100
117
|
)
|
101
|
-
|
102
|
-
|
103
|
-
|
118
|
+
# OpenTherm device
|
119
|
+
if device["name"] != "OnOff":
|
120
|
+
self._check_availability(
|
121
|
+
device, "heater_central", data, "no OpenTherm communication"
|
104
122
|
)
|
105
123
|
|
106
|
-
|
107
|
-
self
|
124
|
+
# Switching groups data
|
125
|
+
self._device_data_switching_group(device, data)
|
126
|
+
# Adam data
|
127
|
+
self._device_data_adam(device, data)
|
128
|
+
# Skip obtaining data for (Adam) secondary thermostats
|
129
|
+
if device["dev_class"] not in ZONE_THERMOSTATS:
|
130
|
+
return data
|
131
|
+
|
132
|
+
# Thermostat data (presets, temperatures etc)
|
133
|
+
self._device_data_climate(device, data)
|
134
|
+
|
135
|
+
return data
|
136
|
+
|
137
|
+
def _check_availability(
|
138
|
+
self, device: DeviceData, dev_class: str, data: DeviceData, message: str
|
108
139
|
) -> None:
|
109
140
|
"""Helper-function for _get_device_data().
|
110
141
|
|
111
|
-
|
142
|
+
Provide availability status for the wired-commected devices.
|
112
143
|
"""
|
113
|
-
if device["dev_class"]
|
114
|
-
|
115
|
-
for member in device["members"]:
|
116
|
-
if self.gw_devices[member]["switches"].get("relay"):
|
117
|
-
counter += 1
|
118
|
-
data["switches"]["relay"] = counter != 0
|
144
|
+
if device["dev_class"] == dev_class:
|
145
|
+
data["available"] = True
|
119
146
|
self._count += 1
|
147
|
+
for item in self._notifications.values():
|
148
|
+
for msg in item.values():
|
149
|
+
if message in msg:
|
150
|
+
data["available"] = False
|
120
151
|
|
121
152
|
def _device_data_adam(self, device: DeviceData, data: DeviceData) -> None:
|
122
153
|
"""Helper-function for _get_device_data().
|
@@ -133,7 +164,7 @@ class SmileData(SmileHelper):
|
|
133
164
|
):
|
134
165
|
data["binary_sensors"]["heating_state"] = self._heating_valves() != 0
|
135
166
|
|
136
|
-
# Show the allowed
|
167
|
+
# Show the allowed regulation_modes and gateway_modes
|
137
168
|
if device["dev_class"] == "gateway":
|
138
169
|
if self._reg_allowed_modes:
|
139
170
|
data["regulation_modes"] = self._reg_allowed_modes
|
@@ -142,7 +173,7 @@ class SmileData(SmileHelper):
|
|
142
173
|
data["gateway_modes"] = self._gw_allowed_modes
|
143
174
|
self._count += 1
|
144
175
|
|
145
|
-
# Control_state, only for Adam
|
176
|
+
# Control_state, only available for Adam primary thermostats
|
146
177
|
if device["dev_class"] in ZONE_THERMOSTATS:
|
147
178
|
loc_id = device["location"]
|
148
179
|
if ctrl_state := self._control_state(loc_id):
|
@@ -213,50 +244,3 @@ class SmileData(SmileHelper):
|
|
213
244
|
all_off = False
|
214
245
|
if all_off:
|
215
246
|
data["select_schedule"] = OFF
|
216
|
-
|
217
|
-
def _check_availability(
|
218
|
-
self, device: DeviceData, dev_class: str, data: DeviceData, message: str
|
219
|
-
) -> None:
|
220
|
-
"""Helper-function for _get_device_data().
|
221
|
-
|
222
|
-
Provide availability status for the wired-commected devices.
|
223
|
-
"""
|
224
|
-
if device["dev_class"] == dev_class:
|
225
|
-
data["available"] = True
|
226
|
-
self._count += 1
|
227
|
-
for item in self._notifications.values():
|
228
|
-
for msg in item.values():
|
229
|
-
if message in msg:
|
230
|
-
data["available"] = False
|
231
|
-
|
232
|
-
def _get_device_data(self, dev_id: str) -> DeviceData:
|
233
|
-
"""Helper-function for _all_device_data() and async_update().
|
234
|
-
|
235
|
-
Provide device-data, based on Location ID (= dev_id), from APPLIANCES.
|
236
|
-
"""
|
237
|
-
device = self.gw_devices[dev_id]
|
238
|
-
data = self._get_measurement_data(dev_id)
|
239
|
-
|
240
|
-
# Check availability of wired-connected devices
|
241
|
-
# Smartmeter
|
242
|
-
self._check_availability(
|
243
|
-
device, "smartmeter", data, "P1 does not seem to be connected"
|
244
|
-
)
|
245
|
-
# OpenTherm device
|
246
|
-
if device["name"] != "OnOff":
|
247
|
-
self._check_availability(
|
248
|
-
device, "heater_central", data, "no OpenTherm communication"
|
249
|
-
)
|
250
|
-
|
251
|
-
# Switching groups data
|
252
|
-
self._device_data_switching_group(device, data)
|
253
|
-
# Adam data
|
254
|
-
self._device_data_adam(device, data)
|
255
|
-
# Skip obtaining data for non master-thermostats
|
256
|
-
if device["dev_class"] not in ZONE_THERMOSTATS:
|
257
|
-
return data
|
258
|
-
|
259
|
-
# Thermostat data (presets, temperatures etc)
|
260
|
-
self._device_data_climate(device, data)
|
261
|
-
|
262
|
-
return data
|