plugwise 0.37.1a2__py3-none-any.whl → 0.37.3__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 +45 -41
- plugwise/smile.py +130 -130
- plugwise/util.py +62 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.3.dist-info}/METADATA +1 -5
- plugwise-0.37.3.dist-info/RECORD +17 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.3.dist-info}/WHEEL +1 -1
- plugwise-0.37.1a2.dist-info/RECORD +0 -17
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.3.dist-info}/LICENSE +0 -0
- {plugwise-0.37.1a2.dist-info → plugwise-0.37.3.dist-info}/top_level.txt +0 -0
plugwise/legacy/smile.py
CHANGED
@@ -145,6 +145,34 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
145
145
|
### API Set and HA Service-related Functions ###
|
146
146
|
########################################################################################################
|
147
147
|
|
148
|
+
async def delete_notification(self) -> None:
|
149
|
+
"""Set-function placeholder for legacy devices."""
|
150
|
+
|
151
|
+
async def set_dhw_mode(self, mode: str) -> None:
|
152
|
+
"""Set-function placeholder for legacy devices."""
|
153
|
+
|
154
|
+
async def set_gateway_mode(self, mode: str) -> None:
|
155
|
+
"""Set-function placeholder for legacy devices."""
|
156
|
+
|
157
|
+
async def set_number_setpoint(self, key: str, temperature: float) -> None:
|
158
|
+
"""Set-function placeholder for legacy devices."""
|
159
|
+
|
160
|
+
async def set_preset(self, _: str, preset: str) -> None:
|
161
|
+
"""Set the given Preset on the relevant Thermostat - from DOMAIN_OBJECTS."""
|
162
|
+
if (presets := self._presets()) is None:
|
163
|
+
raise PlugwiseError("Plugwise: no presets available.") # pragma: no cover
|
164
|
+
if preset not in list(presets):
|
165
|
+
raise PlugwiseError("Plugwise: invalid preset.")
|
166
|
+
|
167
|
+
locator = f'rule/directives/when/then[@icon="{preset}"].../.../...'
|
168
|
+
rule = self._domain_objects.find(locator)
|
169
|
+
data = f'<rules><rule id="{rule.attrib["id"]}"><active>true</active></rule></rules>'
|
170
|
+
|
171
|
+
await self._request(RULES, method="put", data=data)
|
172
|
+
|
173
|
+
async def set_regulation_mode(self, mode: str) -> None:
|
174
|
+
"""Set-function placeholder for legacy devices."""
|
175
|
+
|
148
176
|
async def set_schedule_state(self, _: str, state: str, __: str | None) -> None:
|
149
177
|
"""Activate/deactivate the Schedule.
|
150
178
|
|
@@ -180,35 +208,6 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
180
208
|
|
181
209
|
await self._request(uri, method="put", data=data)
|
182
210
|
|
183
|
-
async def set_preset(self, _: str, preset: str) -> None:
|
184
|
-
"""Set the given Preset on the relevant Thermostat - from DOMAIN_OBJECTS."""
|
185
|
-
if (presets := self._presets()) is None:
|
186
|
-
raise PlugwiseError("Plugwise: no presets available.") # pragma: no cover
|
187
|
-
if preset not in list(presets):
|
188
|
-
raise PlugwiseError("Plugwise: invalid preset.")
|
189
|
-
|
190
|
-
locator = f'rule/directives/when/then[@icon="{preset}"].../.../...'
|
191
|
-
rule = self._domain_objects.find(locator)
|
192
|
-
data = f'<rules><rule id="{rule.attrib["id"]}"><active>true</active></rule></rules>'
|
193
|
-
|
194
|
-
await self._request(RULES, method="put", data=data)
|
195
|
-
|
196
|
-
async def set_temperature(self, setpoint: str, _: dict[str, float]) -> None:
|
197
|
-
"""Set the given Temperature on the relevant Thermostat."""
|
198
|
-
if setpoint is None:
|
199
|
-
raise PlugwiseError(
|
200
|
-
"Plugwise: failed setting temperature: no valid input provided"
|
201
|
-
) # pragma: no cover"
|
202
|
-
|
203
|
-
temperature = str(setpoint)
|
204
|
-
uri = self._thermostat_uri()
|
205
|
-
data = (
|
206
|
-
"<thermostat_functionality><setpoint>"
|
207
|
-
f"{temperature}</setpoint></thermostat_functionality>"
|
208
|
-
)
|
209
|
-
|
210
|
-
await self._request(uri, method="put", data=data)
|
211
|
-
|
212
211
|
async def set_switch_state(
|
213
212
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
214
213
|
) -> None:
|
@@ -250,20 +249,25 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
250
249
|
|
251
250
|
await self._request(uri, method="put", data=data)
|
252
251
|
|
253
|
-
async def
|
254
|
-
"""Set
|
255
|
-
|
256
|
-
|
257
|
-
|
252
|
+
async def set_temperature(self, _: str, items: dict[str, float]) -> None:
|
253
|
+
"""Set the given Temperature on the relevant Thermostat."""
|
254
|
+
setpoint: float | None = None
|
255
|
+
if "setpoint" in items:
|
256
|
+
setpoint = items["setpoint"]
|
258
257
|
|
259
|
-
|
260
|
-
|
258
|
+
if setpoint is None:
|
259
|
+
raise PlugwiseError(
|
260
|
+
"Plugwise: failed setting temperature: no valid input provided"
|
261
|
+
) # pragma: no cover"
|
261
262
|
|
262
|
-
|
263
|
-
|
263
|
+
temperature = str(setpoint)
|
264
|
+
uri = self._thermostat_uri()
|
265
|
+
data = (
|
266
|
+
"<thermostat_functionality><setpoint>"
|
267
|
+
f"{temperature}</setpoint></thermostat_functionality>"
|
268
|
+
)
|
264
269
|
|
265
|
-
|
266
|
-
"""Set-function placeholder for legacy devices."""
|
270
|
+
await self._request(uri, method="put", data=data)
|
267
271
|
|
268
|
-
async def
|
272
|
+
async def set_temperature_offset(self, dev_id: str, offset: float) -> None:
|
269
273
|
"""Set-function placeholder for legacy devices."""
|
plugwise/smile.py
CHANGED
@@ -145,6 +145,93 @@ class SmileAPI(SmileComm, SmileData):
|
|
145
145
|
### API Set and HA Service-related Functions ###
|
146
146
|
########################################################################################################
|
147
147
|
|
148
|
+
async def delete_notification(self) -> None:
|
149
|
+
"""Delete the active Plugwise Notification."""
|
150
|
+
await self._request(NOTIFICATIONS, method="delete")
|
151
|
+
|
152
|
+
async def set_dhw_mode(self, mode: str) -> None:
|
153
|
+
"""Set the domestic hot water heating regulation mode."""
|
154
|
+
if mode not in self._dhw_allowed_modes:
|
155
|
+
raise PlugwiseError("Plugwise: invalid dhw mode.")
|
156
|
+
|
157
|
+
uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control"
|
158
|
+
data = f"<domestic_hot_water_mode_control_functionality><mode>{mode}</mode></domestic_hot_water_mode_control_functionality>"
|
159
|
+
|
160
|
+
await self._request(uri, method="put", data=data)
|
161
|
+
|
162
|
+
async def set_gateway_mode(self, mode: str) -> None:
|
163
|
+
"""Set the gateway mode."""
|
164
|
+
if mode not in self._gw_allowed_modes:
|
165
|
+
raise PlugwiseError("Plugwise: invalid gateway mode.")
|
166
|
+
|
167
|
+
end_time = "2037-04-21T08:00:53.000Z"
|
168
|
+
valid = ""
|
169
|
+
if mode == "away":
|
170
|
+
time_1 = self._domain_objects.find("./gateway/time").text
|
171
|
+
away_time = dt.datetime.fromisoformat(time_1).astimezone(dt.UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
172
|
+
valid = (
|
173
|
+
f"<valid_from>{away_time}</valid_from><valid_to>{end_time}</valid_to>"
|
174
|
+
)
|
175
|
+
if mode == "vacation":
|
176
|
+
time_2 = str(dt.date.today() - dt.timedelta(1))
|
177
|
+
vacation_time = time_2 + "T23:00:00.000Z"
|
178
|
+
valid = f"<valid_from>{vacation_time}</valid_from><valid_to>{end_time}</valid_to>"
|
179
|
+
|
180
|
+
uri = f"{APPLIANCES};id={self.gateway_id}/gateway_mode_control"
|
181
|
+
data = f"<gateway_mode_control_functionality><mode>{mode}</mode>{valid}</gateway_mode_control_functionality>"
|
182
|
+
|
183
|
+
await self._request(uri, method="put", data=data)
|
184
|
+
|
185
|
+
async def set_number_setpoint(self, key: str, temperature: float) -> None:
|
186
|
+
"""Set the max. Boiler or DHW setpoint on the Central Heating boiler."""
|
187
|
+
temp = str(temperature)
|
188
|
+
thermostat_id: str | None = None
|
189
|
+
locator = f'appliance[@id="{self._heater_id}"]/actuator_functionalities/thermostat_functionality'
|
190
|
+
if th_func_list := self._domain_objects.findall(locator):
|
191
|
+
for th_func in th_func_list:
|
192
|
+
if th_func.find("type").text == key:
|
193
|
+
thermostat_id = th_func.attrib["id"]
|
194
|
+
|
195
|
+
if thermostat_id is None:
|
196
|
+
raise PlugwiseError(f"Plugwise: cannot change setpoint, {key} not found.")
|
197
|
+
|
198
|
+
uri = f"{APPLIANCES};id={self._heater_id}/thermostat;id={thermostat_id}"
|
199
|
+
data = f"<thermostat_functionality><setpoint>{temp}</setpoint></thermostat_functionality>"
|
200
|
+
await self._request(uri, method="put", data=data)
|
201
|
+
|
202
|
+
async def set_preset(self, loc_id: str, preset: str) -> None:
|
203
|
+
"""Set the given Preset on the relevant Thermostat - from LOCATIONS."""
|
204
|
+
if (presets := self._presets(loc_id)) is None:
|
205
|
+
raise PlugwiseError("Plugwise: no presets available.") # pragma: no cover
|
206
|
+
if preset not in list(presets):
|
207
|
+
raise PlugwiseError("Plugwise: invalid preset.")
|
208
|
+
|
209
|
+
current_location = self._domain_objects.find(f'location[@id="{loc_id}"]')
|
210
|
+
location_name = current_location.find("name").text
|
211
|
+
location_type = current_location.find("type").text
|
212
|
+
|
213
|
+
uri = f"{LOCATIONS};id={loc_id}"
|
214
|
+
data = (
|
215
|
+
"<locations><location"
|
216
|
+
f' id="{loc_id}"><name>{location_name}</name><type>{location_type}'
|
217
|
+
f"</type><preset>{preset}</preset></location></locations>"
|
218
|
+
)
|
219
|
+
|
220
|
+
await self._request(uri, method="put", data=data)
|
221
|
+
|
222
|
+
async def set_regulation_mode(self, mode: str) -> None:
|
223
|
+
"""Set the heating regulation mode."""
|
224
|
+
if mode not in self._reg_allowed_modes:
|
225
|
+
raise PlugwiseError("Plugwise: invalid regulation mode.")
|
226
|
+
|
227
|
+
uri = f"{APPLIANCES};type=gateway/regulation_mode_control"
|
228
|
+
duration = ""
|
229
|
+
if "bleeding" in mode:
|
230
|
+
duration = "<duration>300</duration>"
|
231
|
+
data = f"<regulation_mode_control_functionality>{duration}<mode>{mode}</mode></regulation_mode_control_functionality>"
|
232
|
+
|
233
|
+
await self._request(uri, method="put", data=data)
|
234
|
+
|
148
235
|
async def set_schedule_state(
|
149
236
|
self,
|
150
237
|
loc_id: str,
|
@@ -219,97 +306,6 @@ class SmileAPI(SmileComm, SmileData):
|
|
219
306
|
|
220
307
|
return etree.tostring(contexts, encoding="unicode").rstrip()
|
221
308
|
|
222
|
-
async def set_preset(self, loc_id: str, preset: str) -> None:
|
223
|
-
"""Set the given Preset on the relevant Thermostat - from LOCATIONS."""
|
224
|
-
if (presets := self._presets(loc_id)) is None:
|
225
|
-
raise PlugwiseError("Plugwise: no presets available.") # pragma: no cover
|
226
|
-
if preset not in list(presets):
|
227
|
-
raise PlugwiseError("Plugwise: invalid preset.")
|
228
|
-
|
229
|
-
current_location = self._domain_objects.find(f'location[@id="{loc_id}"]')
|
230
|
-
location_name = current_location.find("name").text
|
231
|
-
location_type = current_location.find("type").text
|
232
|
-
|
233
|
-
uri = f"{LOCATIONS};id={loc_id}"
|
234
|
-
data = (
|
235
|
-
"<locations><location"
|
236
|
-
f' id="{loc_id}"><name>{location_name}</name><type>{location_type}'
|
237
|
-
f"</type><preset>{preset}</preset></location></locations>"
|
238
|
-
)
|
239
|
-
|
240
|
-
await self._request(uri, method="put", data=data)
|
241
|
-
|
242
|
-
async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None:
|
243
|
-
"""Set the given Temperature on the relevant Thermostat."""
|
244
|
-
setpoint: float | None = None
|
245
|
-
|
246
|
-
if "setpoint" in items:
|
247
|
-
setpoint = items["setpoint"]
|
248
|
-
|
249
|
-
if self.smile(ANNA) and self._cooling_present:
|
250
|
-
if "setpoint_high" not in items:
|
251
|
-
raise PlugwiseError(
|
252
|
-
"Plugwise: failed setting temperature: no valid input provided"
|
253
|
-
)
|
254
|
-
tmp_setpoint_high = items["setpoint_high"]
|
255
|
-
tmp_setpoint_low = items["setpoint_low"]
|
256
|
-
if self._cooling_enabled: # in cooling mode
|
257
|
-
setpoint = tmp_setpoint_high
|
258
|
-
if tmp_setpoint_low != MIN_SETPOINT:
|
259
|
-
raise PlugwiseError(
|
260
|
-
"Plugwise: heating setpoint cannot be changed when in cooling mode"
|
261
|
-
)
|
262
|
-
else: # in heating mode
|
263
|
-
setpoint = tmp_setpoint_low
|
264
|
-
if tmp_setpoint_high != MAX_SETPOINT:
|
265
|
-
raise PlugwiseError(
|
266
|
-
"Plugwise: cooling setpoint cannot be changed when in heating mode"
|
267
|
-
)
|
268
|
-
|
269
|
-
if setpoint is None:
|
270
|
-
raise PlugwiseError(
|
271
|
-
"Plugwise: failed setting temperature: no valid input provided"
|
272
|
-
) # pragma: no cover"
|
273
|
-
|
274
|
-
temperature = str(setpoint)
|
275
|
-
uri = self._thermostat_uri(loc_id)
|
276
|
-
data = (
|
277
|
-
"<thermostat_functionality><setpoint>"
|
278
|
-
f"{temperature}</setpoint></thermostat_functionality>"
|
279
|
-
)
|
280
|
-
|
281
|
-
await self._request(uri, method="put", data=data)
|
282
|
-
|
283
|
-
async def set_number_setpoint(self, key: str, temperature: float) -> None:
|
284
|
-
"""Set the max. Boiler or DHW setpoint on the Central Heating boiler."""
|
285
|
-
temp = str(temperature)
|
286
|
-
thermostat_id: str | None = None
|
287
|
-
locator = f'appliance[@id="{self._heater_id}"]/actuator_functionalities/thermostat_functionality'
|
288
|
-
if th_func_list := self._domain_objects.findall(locator):
|
289
|
-
for th_func in th_func_list:
|
290
|
-
if th_func.find("type").text == key:
|
291
|
-
thermostat_id = th_func.attrib["id"]
|
292
|
-
|
293
|
-
if thermostat_id is None:
|
294
|
-
raise PlugwiseError(f"Plugwise: cannot change setpoint, {key} not found.")
|
295
|
-
|
296
|
-
uri = f"{APPLIANCES};id={self._heater_id}/thermostat;id={thermostat_id}"
|
297
|
-
data = f"<thermostat_functionality><setpoint>{temp}</setpoint></thermostat_functionality>"
|
298
|
-
await self._request(uri, method="put", data=data)
|
299
|
-
|
300
|
-
async def set_temperature_offset(self, dev_id: str, offset: float) -> None:
|
301
|
-
"""Set the Temperature offset for thermostats that support this feature."""
|
302
|
-
if dev_id not in self.therms_with_offset_func:
|
303
|
-
raise PlugwiseError(
|
304
|
-
"Plugwise: this device does not have temperature-offset capability."
|
305
|
-
)
|
306
|
-
|
307
|
-
value = str(offset)
|
308
|
-
uri = f"{APPLIANCES};id={dev_id}/offset;type=temperature_offset"
|
309
|
-
data = f"<offset_functionality><offset>{value}</offset></offset_functionality>"
|
310
|
-
|
311
|
-
await self._request(uri, method="put", data=data)
|
312
|
-
|
313
309
|
async def set_switch_state(
|
314
310
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
315
311
|
) -> None:
|
@@ -374,52 +370,56 @@ class SmileAPI(SmileComm, SmileData):
|
|
374
370
|
|
375
371
|
await self._request(uri, method="put", data=data)
|
376
372
|
|
377
|
-
async def
|
378
|
-
"""Set the
|
379
|
-
|
380
|
-
raise PlugwiseError("Plugwise: invalid gateway mode.")
|
381
|
-
|
382
|
-
end_time = "2037-04-21T08:00:53.000Z"
|
383
|
-
valid = ""
|
384
|
-
if mode == "away":
|
385
|
-
time_1 = self._domain_objects.find("./gateway/time").text
|
386
|
-
away_time = dt.datetime.fromisoformat(time_1).astimezone(dt.UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
387
|
-
valid = (
|
388
|
-
f"<valid_from>{away_time}</valid_from><valid_to>{end_time}</valid_to>"
|
389
|
-
)
|
390
|
-
if mode == "vacation":
|
391
|
-
time_2 = str(dt.date.today() - dt.timedelta(1))
|
392
|
-
vacation_time = time_2 + "T23:00:00.000Z"
|
393
|
-
valid = f"<valid_from>{vacation_time}</valid_from><valid_to>{end_time}</valid_to>"
|
373
|
+
async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None:
|
374
|
+
"""Set the given Temperature on the relevant Thermostat."""
|
375
|
+
setpoint: float | None = None
|
394
376
|
|
395
|
-
|
396
|
-
|
377
|
+
if "setpoint" in items:
|
378
|
+
setpoint = items["setpoint"]
|
397
379
|
|
398
|
-
|
380
|
+
if self.smile(ANNA) and self._cooling_present:
|
381
|
+
if "setpoint_high" not in items:
|
382
|
+
raise PlugwiseError(
|
383
|
+
"Plugwise: failed setting temperature: no valid input provided"
|
384
|
+
)
|
385
|
+
tmp_setpoint_high = items["setpoint_high"]
|
386
|
+
tmp_setpoint_low = items["setpoint_low"]
|
387
|
+
if self._cooling_enabled: # in cooling mode
|
388
|
+
setpoint = tmp_setpoint_high
|
389
|
+
if tmp_setpoint_low != MIN_SETPOINT:
|
390
|
+
raise PlugwiseError(
|
391
|
+
"Plugwise: heating setpoint cannot be changed when in cooling mode"
|
392
|
+
)
|
393
|
+
else: # in heating mode
|
394
|
+
setpoint = tmp_setpoint_low
|
395
|
+
if tmp_setpoint_high != MAX_SETPOINT:
|
396
|
+
raise PlugwiseError(
|
397
|
+
"Plugwise: cooling setpoint cannot be changed when in heating mode"
|
398
|
+
)
|
399
399
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
400
|
+
if setpoint is None:
|
401
|
+
raise PlugwiseError(
|
402
|
+
"Plugwise: failed setting temperature: no valid input provided"
|
403
|
+
) # pragma: no cover"
|
404
404
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
405
|
+
temperature = str(setpoint)
|
406
|
+
uri = self._thermostat_uri(loc_id)
|
407
|
+
data = (
|
408
|
+
"<thermostat_functionality><setpoint>"
|
409
|
+
f"{temperature}</setpoint></thermostat_functionality>"
|
410
|
+
)
|
410
411
|
|
411
412
|
await self._request(uri, method="put", data=data)
|
412
413
|
|
413
|
-
async def
|
414
|
-
"""Set the
|
415
|
-
if
|
416
|
-
raise PlugwiseError(
|
414
|
+
async def set_temperature_offset(self, dev_id: str, offset: float) -> None:
|
415
|
+
"""Set the Temperature offset for thermostats that support this feature."""
|
416
|
+
if dev_id not in self.therms_with_offset_func:
|
417
|
+
raise PlugwiseError(
|
418
|
+
"Plugwise: this device does not have temperature-offset capability."
|
419
|
+
)
|
417
420
|
|
418
|
-
|
419
|
-
|
421
|
+
value = str(offset)
|
422
|
+
uri = f"{APPLIANCES};id={dev_id}/offset;type=temperature_offset"
|
423
|
+
data = f"<offset_functionality><offset>{value}</offset></offset_functionality>"
|
420
424
|
|
421
425
|
await self._request(uri, method="put", data=data)
|
422
|
-
|
423
|
-
async def delete_notification(self) -> None:
|
424
|
-
"""Delete the active Plugwise Notification."""
|
425
|
-
await self._request(NOTIFICATIONS, method="delete")
|
plugwise/util.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Plugwise protocol helpers."""
|
2
2
|
from __future__ import annotations
|
3
3
|
|
4
|
+
import datetime as dt
|
4
5
|
import re
|
5
6
|
|
6
7
|
from plugwise.constants import (
|
@@ -8,6 +9,7 @@ from plugwise.constants import (
|
|
8
9
|
ELECTRIC_POTENTIAL_VOLT,
|
9
10
|
ENERGY_KILO_WATT_HOUR,
|
10
11
|
HW_MODELS,
|
12
|
+
OBSOLETE_MEASUREMENTS,
|
11
13
|
PERCENTAGE,
|
12
14
|
POWER_WATT,
|
13
15
|
SPECIAL_FORMAT,
|
@@ -17,6 +19,50 @@ from plugwise.constants import (
|
|
17
19
|
)
|
18
20
|
|
19
21
|
from defusedxml import ElementTree as etree
|
22
|
+
from munch import Munch
|
23
|
+
|
24
|
+
|
25
|
+
def check_alternative_location(loc: Munch, legacy: bool) -> Munch:
|
26
|
+
"""Try."""
|
27
|
+
if in_alternative_location(loc, legacy):
|
28
|
+
# Avoid double processing by skipping one peak-list option
|
29
|
+
if loc.peak_select == "nl_offpeak":
|
30
|
+
loc.found = False
|
31
|
+
return loc
|
32
|
+
|
33
|
+
loc.locator = (
|
34
|
+
f'./{loc.log_type}[type="{loc.measurement}"]/period/measurement'
|
35
|
+
)
|
36
|
+
if legacy:
|
37
|
+
loc.locator = (
|
38
|
+
f"./{loc.meas_list[0]}_{loc.log_type}/"
|
39
|
+
f'measurement[@directionality="{loc.meas_list[1]}"]'
|
40
|
+
)
|
41
|
+
|
42
|
+
if loc.logs.find(loc.locator) is None:
|
43
|
+
loc.found = False
|
44
|
+
return loc
|
45
|
+
|
46
|
+
return loc
|
47
|
+
|
48
|
+
loc.found = False
|
49
|
+
return loc
|
50
|
+
|
51
|
+
|
52
|
+
def in_alternative_location(loc: Munch, legacy: bool) -> bool:
|
53
|
+
"""Look for P1 gas_consumed or phase data (without tariff).
|
54
|
+
|
55
|
+
For legacy look for P1 legacy electricity_point_meter or gas_*_meter data.
|
56
|
+
"""
|
57
|
+
present = "log" in loc.log_type and (
|
58
|
+
"gas" in loc.measurement or "phase" in loc.measurement
|
59
|
+
)
|
60
|
+
if legacy:
|
61
|
+
present = "meter" in loc.log_type and (
|
62
|
+
"point" in loc.log_type or "gas" in loc.measurement
|
63
|
+
)
|
64
|
+
|
65
|
+
return present
|
20
66
|
|
21
67
|
|
22
68
|
def check_heater_central(xml: etree) -> str:
|
@@ -128,6 +174,22 @@ def return_valid(value: etree | None, default: etree) -> etree:
|
|
128
174
|
return value if value is not None else default
|
129
175
|
|
130
176
|
|
177
|
+
def skip_obsolete_measurements(xml: etree, measurement: str) -> bool:
|
178
|
+
"""Skipping known obsolete measurements."""
|
179
|
+
locator = f".//logs/point_log[type='{measurement}']/updated_date"
|
180
|
+
if (
|
181
|
+
measurement in OBSOLETE_MEASUREMENTS
|
182
|
+
and (updated_date_key := xml.find(locator))
|
183
|
+
is not None
|
184
|
+
):
|
185
|
+
updated_date = updated_date_key.text.split("T")[0]
|
186
|
+
date_1 = dt.datetime.strptime(updated_date, "%Y-%m-%d")
|
187
|
+
date_2 = dt.datetime.now()
|
188
|
+
return int((date_2 - date_1).days) > 7
|
189
|
+
|
190
|
+
return False
|
191
|
+
|
192
|
+
|
131
193
|
# NOTE: this function version_to_model is shared between Smile and USB
|
132
194
|
def version_to_model(version: str | None) -> str | None:
|
133
195
|
"""Translate hardware_version to device type."""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: plugwise
|
3
|
-
Version: 0.37.
|
3
|
+
Version: 0.37.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
|
@@ -35,18 +35,14 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
35
35
|
Classifier: Intended Audience :: Developers
|
36
36
|
Classifier: License :: OSI Approved :: MIT License
|
37
37
|
Classifier: Operating System :: OS Independent
|
38
|
-
Classifier: Programming Language :: Python :: 3.11
|
39
38
|
Classifier: Programming Language :: Python :: 3.12
|
40
39
|
Classifier: Topic :: Home Automation
|
41
40
|
Requires-Python: >=3.11.0
|
42
41
|
Description-Content-Type: text/markdown
|
43
42
|
License-File: LICENSE
|
44
43
|
Requires-Dist: aiohttp
|
45
|
-
Requires-Dist: async-timeout
|
46
|
-
Requires-Dist: crcmod
|
47
44
|
Requires-Dist: defusedxml
|
48
45
|
Requires-Dist: munch
|
49
|
-
Requires-Dist: pyserial
|
50
46
|
Requires-Dist: python-dateutil
|
51
47
|
|
52
48
|
# Plugwise python module
|
@@ -0,0 +1,17 @@
|
|
1
|
+
plugwise/__init__.py,sha256=A0XCK9xmhzLGSsy0bAGr2tnKfGDUlJ7WzQfeIqCCd8A,14004
|
2
|
+
plugwise/common.py,sha256=P4sUYzgVcFsIR2DmQxeVeOiZvFZWpZXgwHA3XRc1Sx0,12538
|
3
|
+
plugwise/constants.py,sha256=adi2UFHJUcPRWNz1bn155NC9DLzhYbeNriMKTpSyWAg,16574
|
4
|
+
plugwise/data.py,sha256=OTkPJ80b1L1h_TeddfvmUPX1-jBacTjbHKD0XgUIfeg,8965
|
5
|
+
plugwise/exceptions.py,sha256=rymGtWnXosdFkzSehc_41HVl2jXJEg_9Hq9APDEUwrk,1223
|
6
|
+
plugwise/helper.py,sha256=bNT_Tdc0LAxmVZEikMgKm461jeGP-WY-cI1r3JGgIcI,44037
|
7
|
+
plugwise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
plugwise/smile.py,sha256=_1VFG81pfuy673M8kGQVfpSX3Kdin2Xl7-KsUXNpGlY,16985
|
9
|
+
plugwise/util.py,sha256=w24CiW-xSV3ARZAeNYZ9T5C4kJpYGCcBoIlX2BhNOYg,6618
|
10
|
+
plugwise/legacy/data.py,sha256=y7gBG9Sg40lltB3B5CpWPtAXggMbcx2IMay-mwvOB1M,2986
|
11
|
+
plugwise/legacy/helper.py,sha256=tSrqA2UeMO7HSYudQHAxBThBTevAOKIxHqN6NCN-r5k,18947
|
12
|
+
plugwise/legacy/smile.py,sha256=JlJGg2TNl1uBeW-vLzsHzrpeizKpZqN9coiDYcedHuc,10347
|
13
|
+
plugwise-0.37.3.dist-info/LICENSE,sha256=mL22BjmXtg_wnoDnnaqps5_Bg_VGj_yHueX5lsKwbCc,1144
|
14
|
+
plugwise-0.37.3.dist-info/METADATA,sha256=Tgjez9bNSQ3C-UrLQR8cL0aZwzojQX-PYmSCaZht6N8,8939
|
15
|
+
plugwise-0.37.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
16
|
+
plugwise-0.37.3.dist-info/top_level.txt,sha256=MYOmktMFf8ZmX6_OE1y9MoCZFfY-L8DA0F2tA2IvE4s,9
|
17
|
+
plugwise-0.37.3.dist-info/RECORD,,
|
@@ -1,17 +0,0 @@
|
|
1
|
-
plugwise/__init__.py,sha256=A0XCK9xmhzLGSsy0bAGr2tnKfGDUlJ7WzQfeIqCCd8A,14004
|
2
|
-
plugwise/common.py,sha256=Apn_pgiOGypwwc5aCwIXFeOceLel0CysHHxj_GVDej8,5914
|
3
|
-
plugwise/constants.py,sha256=_IO-MQQu_gFZ71VTNlmNY5Lqr6u0t5rWaQTQJqe_xRc,16467
|
4
|
-
plugwise/data.py,sha256=wed7BfOI7QCIFlen6zv5jx4zG3SRM8YDoeGP7XfN8nA,9495
|
5
|
-
plugwise/exceptions.py,sha256=rymGtWnXosdFkzSehc_41HVl2jXJEg_9Hq9APDEUwrk,1223
|
6
|
-
plugwise/helper.py,sha256=_hpaQrrS2npI2f0UwwthU-pq4kzspnkurwIH4ewKJ7Y,51658
|
7
|
-
plugwise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
plugwise/smile.py,sha256=_86nL1mVaN0GJ7XwDzlVFjkDorsdfIFLFJ2vs2woI_Y,16985
|
9
|
-
plugwise/util.py,sha256=PTAwKODUcTL7C87CF-ul_6nC2TcQrHVXEZb5qY5sy0I,4756
|
10
|
-
plugwise/legacy/data.py,sha256=bomP7t2NwNIhSRGBZRXLuAHnXUiIqtpnHWseovXkRK0,3614
|
11
|
-
plugwise/legacy/helper.py,sha256=RkrgchFtju32GeJ8F0eeOEVfJWP-etWFkJyo6VhvnxQ,26105
|
12
|
-
plugwise/legacy/smile.py,sha256=49vJtlToio9StrkpNeztOAJ1mRtqrDX463APxFTtGd4,10238
|
13
|
-
plugwise-0.37.1a2.dist-info/LICENSE,sha256=mL22BjmXtg_wnoDnnaqps5_Bg_VGj_yHueX5lsKwbCc,1144
|
14
|
-
plugwise-0.37.1a2.dist-info/METADATA,sha256=tRECCBQ9XJak1ZjLkAnoqxFmM5wjFd6ncDz3Spw3jQo,9067
|
15
|
-
plugwise-0.37.1a2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
16
|
-
plugwise-0.37.1a2.dist-info/top_level.txt,sha256=MYOmktMFf8ZmX6_OE1y9MoCZFfY-L8DA0F2tA2IvE4s,9
|
17
|
-
plugwise-0.37.1a2.dist-info/RECORD,,
|
File without changes
|
File without changes
|