aioWiserHeatAPI 1.7.2__tar.gz → 1.7.4__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.
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/PKG-INFO +14 -6
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/README.md +12 -4
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/__init__.py +1 -1
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/button_panel.py +6 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/const.py +5 -1
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/devices.py +35 -0
- aiowiserheatapi-1.7.4/aioWiserHeatAPI/evse.py +106 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/automations.py +36 -16
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/battery.py +8 -2
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/opentherm.py +6 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/rest_controller.py +5 -2
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/room.py +90 -15
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/wiserhub.py +7 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/PKG-INFO +14 -6
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/SOURCES.txt +1 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/setup.py +1 -1
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/LICENSE +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/automation.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/binary_sensor.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/boiler_interface.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/cli.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/discovery.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/exceptions.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/heating.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/heating_actuator.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/__init__.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/capabilities.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/cloud.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/device.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/equipment.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/extra_config.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/firmware.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/gps.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/misc.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/network.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/signal.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/special_times.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/status.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/temp.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/threshold.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/uiconfiguration.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/version.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/helpers/zigbee.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/hot_water.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/light.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/moments.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/ptc.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/pte.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/refactor.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/roomstat.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/schedule.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/shutter.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/smartplug.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/smartvalve.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/smokealarm.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/system.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/temp_humidity.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI/ufh.py +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/dependency_links.txt +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/entry_points.txt +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/requires.txt +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/top_level.txt +0 -0
- {aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/setup.cfg +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioWiserHeatAPI
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: An AsyncIO API for controlling the Drayton Wiser Heating system
|
|
5
5
|
Home-page: https://github.com/msp1974/aioWiserHeatAPI
|
|
6
6
|
Author: Mark Parker
|
|
7
7
|
Author-email: msparker@sky.com
|
|
8
8
|
Classifier: Development Status :: 5 - Production/Stable
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Requires-Python: >=3.12
|
|
@@ -28,7 +28,7 @@ Dynamic: requires-dist
|
|
|
28
28
|
Dynamic: requires-python
|
|
29
29
|
Dynamic: summary
|
|
30
30
|
|
|
31
|
-
# Drayton Wiser Hub API Async v1.7.
|
|
31
|
+
# Drayton Wiser Hub API Async v1.7.4
|
|
32
32
|
|
|
33
33
|
This repository contains a simple API which queries the Drayton Wiser Heating sysystem used in the UK.
|
|
34
34
|
|
|
@@ -37,7 +37,7 @@ The API also supports Smart Plugs and initial basic functionality for Shutter an
|
|
|
37
37
|
|
|
38
38
|
## Requirements
|
|
39
39
|
|
|
40
|
-
Requires a minimum of Python v3.
|
|
40
|
+
Requires a minimum of Python v3.12
|
|
41
41
|
|
|
42
42
|
## Installation
|
|
43
43
|
|
|
@@ -82,9 +82,17 @@ Documentation available in [info.md](https://github.com/msp1974/wiserHeatAPIv2/b
|
|
|
82
82
|
|
|
83
83
|
## Changelog
|
|
84
84
|
|
|
85
|
-
### v1.7.
|
|
85
|
+
### v1.7.4
|
|
86
86
|
|
|
87
|
-
-
|
|
87
|
+
- Added: optional scheduled lower-temperature provider for Passive Mode
|
|
88
|
+
- Added: EVSE device support
|
|
89
|
+
- Added: Button panel battery status
|
|
90
|
+
- Added: Support for the `OffAfter2s` LED indicator mode
|
|
91
|
+
- Fix: Refine room heating state for OpenTherm systems
|
|
92
|
+
|
|
93
|
+
### v1.7.3
|
|
94
|
+
|
|
95
|
+
- Fix: Set min f/w version of 4.42.22 for https on gen2 hubs
|
|
88
96
|
|
|
89
97
|
### v1.7.0
|
|
90
98
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Drayton Wiser Hub API Async v1.7.
|
|
1
|
+
# Drayton Wiser Hub API Async v1.7.4
|
|
2
2
|
|
|
3
3
|
This repository contains a simple API which queries the Drayton Wiser Heating sysystem used in the UK.
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ The API also supports Smart Plugs and initial basic functionality for Shutter an
|
|
|
7
7
|
|
|
8
8
|
## Requirements
|
|
9
9
|
|
|
10
|
-
Requires a minimum of Python v3.
|
|
10
|
+
Requires a minimum of Python v3.12
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -52,9 +52,17 @@ Documentation available in [info.md](https://github.com/msp1974/wiserHeatAPIv2/b
|
|
|
52
52
|
|
|
53
53
|
## Changelog
|
|
54
54
|
|
|
55
|
-
### v1.7.
|
|
55
|
+
### v1.7.4
|
|
56
56
|
|
|
57
|
-
-
|
|
57
|
+
- Added: optional scheduled lower-temperature provider for Passive Mode
|
|
58
|
+
- Added: EVSE device support
|
|
59
|
+
- Added: Button panel battery status
|
|
60
|
+
- Added: Support for the `OffAfter2s` LED indicator mode
|
|
61
|
+
- Fix: Refine room heating state for OpenTherm systems
|
|
62
|
+
|
|
63
|
+
### v1.7.3
|
|
64
|
+
|
|
65
|
+
- Fix: Set min f/w version of 4.42.22 for https on gen2 hubs
|
|
58
66
|
|
|
59
67
|
### v1.7.0
|
|
60
68
|
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
+
from .helpers.battery import _WiserBattery
|
|
3
4
|
from .helpers.device import _WiserDevice
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class _WiserButtonPanel(_WiserDevice):
|
|
7
8
|
"""Class representing a Wiser Button Panel"""
|
|
8
9
|
|
|
10
|
+
@property
|
|
11
|
+
def battery(self) -> _WiserBattery:
|
|
12
|
+
"""Get the battery information for the button panel."""
|
|
13
|
+
return _WiserBattery(self._data)
|
|
14
|
+
|
|
9
15
|
@property
|
|
10
16
|
def number_of_gangs(self) -> list[int]:
|
|
11
17
|
"""Number of gangs."""
|
|
@@ -46,6 +46,7 @@ TEXT_HEATING = "Heating"
|
|
|
46
46
|
TEXT_LEVEL = "Level"
|
|
47
47
|
TEXT_LIGHTING = "Lighting"
|
|
48
48
|
TEXT_MANUAL = "Manual"
|
|
49
|
+
TEXT_MODULATING = "Modulating"
|
|
49
50
|
TEXT_NO_CHANGE = "NoChange"
|
|
50
51
|
TEXT_OFF = "Off"
|
|
51
52
|
TEXT_ON = "On"
|
|
@@ -68,6 +69,7 @@ TEXT_REVERSE = "ReverseWithLoad"
|
|
|
68
69
|
TEXT_CONSISTENT = "ConsistentWithLoad"
|
|
69
70
|
TEXT_ALWAYSON = "AlwaysOn"
|
|
70
71
|
TEXT_ALWAYSOFF = "AlwaysOff"
|
|
72
|
+
TEXT_OFF_AFTER_2S = "OffAfter2s"
|
|
71
73
|
|
|
72
74
|
TEXT_LASTON = "LastOnBrightness"
|
|
73
75
|
TEXT_LEVELPERCENT = "LevelPercent"
|
|
@@ -81,7 +83,7 @@ SPECIAL_DAYS = [TEXT_WEEKDAYS, TEXT_WEEKENDS, TEXT_ALL]
|
|
|
81
83
|
SPECIAL_TIMES = {"Sunrise": 3000, "Sunset": 4000}
|
|
82
84
|
|
|
83
85
|
OPENTHERMV2_MIN_VERSION = Version("4.32.47")
|
|
84
|
-
HUB_GEN2_MIN_HTTPS_VERSION = Version("4.42.
|
|
86
|
+
HUB_GEN2_MIN_HTTPS_VERSION = Version("4.42.22")
|
|
85
87
|
|
|
86
88
|
# Wiser Hub Rest Api URL Constants
|
|
87
89
|
WISERHUBURL = "{}:{}/data/v2/"
|
|
@@ -108,6 +110,7 @@ WISERSMOKEALARM = "SmokeAlarmDevice/{}"
|
|
|
108
110
|
WISERBINARYSENSOR = "BinarySensor/{}"
|
|
109
111
|
WISERBOILERINTERFACE = "BoilerInterface/{}"
|
|
110
112
|
WISERBUTTONPANEL = "ButtonPanel/{}"
|
|
113
|
+
WISEREQUIPMENT = "Equipment/{}"
|
|
111
114
|
WISERTHRESHOLDSENSOR = "ThresholdSensor/{}"
|
|
112
115
|
WISERUICONFIGURATION = "UIConfiguration/{}"
|
|
113
116
|
|
|
@@ -144,6 +147,7 @@ class WiserLightLedIndicatorEnum(enum.Enum):
|
|
|
144
147
|
consistent = TEXT_CONSISTENT
|
|
145
148
|
allwayson = TEXT_ALWAYSON
|
|
146
149
|
allwaysoff = TEXT_ALWAYSOFF
|
|
150
|
+
offafter2s = TEXT_OFF_AFTER_2S
|
|
147
151
|
|
|
148
152
|
|
|
149
153
|
class WiserLightPowerOnBehaviourEnum(enum.Enum):
|
|
@@ -28,6 +28,7 @@ from .const import (
|
|
|
28
28
|
WISERUFHCONTROLLER,
|
|
29
29
|
WISERUICONFIGURATION,
|
|
30
30
|
)
|
|
31
|
+
from .evse import _WiserEVSE, _WiserEVSECollection
|
|
31
32
|
from .heating_actuator import _WiserHeatingActuator, _WiserHeatingActuatorCollection
|
|
32
33
|
from .helpers.device import _WiserDevice
|
|
33
34
|
from .helpers.threshold import (
|
|
@@ -147,6 +148,12 @@ PRODUCT_TYPE_CONFIG = {
|
|
|
147
148
|
collection=_WiserTempHumidityCollection,
|
|
148
149
|
endpoint=None,
|
|
149
150
|
),
|
|
151
|
+
# EVSE devices have no separate EVSE domain section. Their live data is
|
|
152
|
+
# published directly as an Equipment record linked to the generic Device.
|
|
153
|
+
"EVSE": DeviceConfig(
|
|
154
|
+
device_class=_WiserEVSE,
|
|
155
|
+
collection=_WiserEVSECollection,
|
|
156
|
+
),
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
ANCILLARY_SENSOR_CONFIG = {
|
|
@@ -226,6 +233,26 @@ class _WiserDeviceCollection:
|
|
|
226
233
|
)
|
|
227
234
|
)
|
|
228
235
|
|
|
236
|
+
elif device_type == "EVSE":
|
|
237
|
+
device_config = PRODUCT_TYPE_CONFIG[device_type]
|
|
238
|
+
for equipment in self._equipment_data:
|
|
239
|
+
if equipment.get("DeviceApplicationInstanceType") != "EVSE":
|
|
240
|
+
continue
|
|
241
|
+
device_info_data = self._get_device_info_by_id(
|
|
242
|
+
equipment.get("DeviceApplicationInstanceId")
|
|
243
|
+
)
|
|
244
|
+
if device_info_data is None:
|
|
245
|
+
continue
|
|
246
|
+
self._device_collection[device_type]._items.append(
|
|
247
|
+
device_config.device_class(
|
|
248
|
+
self._wiser_rest_controller,
|
|
249
|
+
device_config.endpoint,
|
|
250
|
+
device_info_data,
|
|
251
|
+
equipment,
|
|
252
|
+
None,
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
|
|
229
256
|
elif device_type_data := self._domain_data.get(device_type):
|
|
230
257
|
# We have this device type in data
|
|
231
258
|
device_config = PRODUCT_TYPE_CONFIG[device_type]
|
|
@@ -323,6 +350,14 @@ class _WiserDeviceCollection:
|
|
|
323
350
|
except KeyError:
|
|
324
351
|
return None
|
|
325
352
|
|
|
353
|
+
@property
|
|
354
|
+
def evse(self) -> _WiserEVSECollection:
|
|
355
|
+
"""Return all EV charging socket devices."""
|
|
356
|
+
try:
|
|
357
|
+
return self._device_collection["EVSE"]
|
|
358
|
+
except KeyError:
|
|
359
|
+
return None
|
|
360
|
+
|
|
326
361
|
@property
|
|
327
362
|
def lights(self) -> _WiserLightCollection:
|
|
328
363
|
"""Return all lights"""
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Support for Wiser EV charging socket equipment."""
|
|
2
|
+
|
|
3
|
+
from .const import TEXT_UNKNOWN, WISEREQUIPMENT
|
|
4
|
+
from .helpers.device import _WiserDevice
|
|
5
|
+
from .helpers.equipment import _WiserEquipment
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class _WiserEVSE(_WiserDevice):
|
|
9
|
+
"""A Wiser EVSE represented by a Device and an Equipment record."""
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def equipment(self) -> _WiserEquipment:
|
|
13
|
+
"""Return the EVSE equipment data."""
|
|
14
|
+
return _WiserEquipment(self._device_type_data)
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def equipment_id(self) -> int:
|
|
18
|
+
"""Return the equipment record id."""
|
|
19
|
+
return self.equipment.id
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def instantaneous_power(self) -> int | None:
|
|
23
|
+
"""Return current active power in watts."""
|
|
24
|
+
power = self.equipment.power
|
|
25
|
+
return (
|
|
26
|
+
power.total_active_power
|
|
27
|
+
if power.total_active_power is not None
|
|
28
|
+
else power.active_power
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def delivered_power(self) -> int | None:
|
|
33
|
+
"""Return accumulated delivered energy in watt-hours."""
|
|
34
|
+
return self.equipment.power.current_summation_delivered
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def available_control_states(self) -> list[str]:
|
|
38
|
+
"""Return control states accepted by the hub for this equipment."""
|
|
39
|
+
return self._device_type_data.get("AvailableControlState") or []
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def control_state(self) -> str:
|
|
43
|
+
"""Return the current load control state."""
|
|
44
|
+
return self._device_type_data.get(
|
|
45
|
+
"ControlState",
|
|
46
|
+
self._device_type_data.get(
|
|
47
|
+
"LoadStateCommandPrio",
|
|
48
|
+
self._device_type_data.get("LoadStateStatus", TEXT_UNKNOWN),
|
|
49
|
+
),
|
|
50
|
+
) or TEXT_UNKNOWN
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def is_on(self) -> bool:
|
|
54
|
+
"""Return whether charging has not been force-disabled."""
|
|
55
|
+
return self.control_state not in {
|
|
56
|
+
TEXT_UNKNOWN,
|
|
57
|
+
"consumptionForceOff",
|
|
58
|
+
"Off",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async def _set_control_state(self, state: str) -> bool:
|
|
62
|
+
"""Set one of the control states advertised by the equipment."""
|
|
63
|
+
if state not in self.available_control_states:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"{state} is not a valid EVSE control state. "
|
|
66
|
+
f"Valid states are {self.available_control_states}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
result = await self._wiser_rest_controller._send_command(
|
|
70
|
+
WISEREQUIPMENT.format(self.equipment_id), {"ControlState": state}
|
|
71
|
+
)
|
|
72
|
+
if result:
|
|
73
|
+
# Some hub versions return only the changed field. Preserve the
|
|
74
|
+
# equipment telemetry and advertised states until the next poll.
|
|
75
|
+
self._device_type_data = {**self._device_type_data, **result}
|
|
76
|
+
return True
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
async def turn_on(self) -> bool:
|
|
80
|
+
"""Return the EVSE to its normal self-managed state."""
|
|
81
|
+
return await self._set_control_state("selfManagement")
|
|
82
|
+
|
|
83
|
+
async def turn_off(self) -> bool:
|
|
84
|
+
"""Force EVSE consumption off."""
|
|
85
|
+
return await self._set_control_state("consumptionForceOff")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class _WiserEVSECollection:
|
|
89
|
+
"""Collection of Wiser EV charging socket devices."""
|
|
90
|
+
|
|
91
|
+
def __init__(self):
|
|
92
|
+
self._items = []
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def all(self) -> list[_WiserEVSE]:
|
|
96
|
+
"""Return all EVSE devices."""
|
|
97
|
+
return list(self._items)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def count(self) -> int:
|
|
101
|
+
"""Return the number of EVSE devices."""
|
|
102
|
+
return len(self.all)
|
|
103
|
+
|
|
104
|
+
def get_by_id(self, device_id: int) -> _WiserEVSE | None:
|
|
105
|
+
"""Return an EVSE by its physical device id."""
|
|
106
|
+
return next((device for device in self.all if device.id == device_id), None)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
3
|
-
from ..const import WiserHeatingModeEnum
|
|
4
2
|
from ..heating import _WiserHeatingChannelCollection
|
|
5
3
|
from ..hot_water import _WiserHotwater
|
|
6
4
|
from ..rest_controller import _WiserRestController
|
|
@@ -62,6 +60,9 @@ class _WiserHeatingChannelAutomations:
|
|
|
62
60
|
passive_mode_increment = (
|
|
63
61
|
self._wiser_rest_controller._api_parameters.passive_mode_increment
|
|
64
62
|
)
|
|
63
|
+
temperature_limiter_provider = (
|
|
64
|
+
self._wiser_rest_controller._api_parameters.passive_mode_temperature_limiter_provider
|
|
65
|
+
)
|
|
65
66
|
# iterate each heating channel
|
|
66
67
|
for heating_channel in self._heating_channels:
|
|
67
68
|
passive_rooms = [
|
|
@@ -69,7 +70,7 @@ class _WiserHeatingChannelAutomations:
|
|
|
69
70
|
for room in self._rooms
|
|
70
71
|
if room.is_passive_mode
|
|
71
72
|
and room.id in heating_channel.room_ids
|
|
72
|
-
and room.current_temperature
|
|
73
|
+
and room.current_temperature is not None
|
|
73
74
|
]
|
|
74
75
|
|
|
75
76
|
# If no passive mode rooms
|
|
@@ -94,20 +95,39 @@ class _WiserHeatingChannelAutomations:
|
|
|
94
95
|
for room in passive_rooms:
|
|
95
96
|
# If room is boosted do not override
|
|
96
97
|
if not room.is_boosted:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
temperature_limited = False
|
|
99
|
+
if temperature_limiter_provider is not None:
|
|
100
|
+
try:
|
|
101
|
+
temperature_limited = bool(
|
|
102
|
+
temperature_limiter_provider(room)
|
|
103
|
+
)
|
|
104
|
+
except Exception:
|
|
105
|
+
_LOGGER.warning(
|
|
106
|
+
"Ignoring invalid Passive Mode temperature "
|
|
107
|
+
"limiter for %s",
|
|
108
|
+
room.name,
|
|
109
|
+
exc_info=True,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if temperature_limited:
|
|
113
|
+
# External Temperature Limiter rooms keep Wiser heat
|
|
114
|
+
# demand at the lower threshold. Their assigned valve
|
|
115
|
+
# is controlled independently by the integration.
|
|
116
|
+
target_temp = room.passive_mode_lower_temp
|
|
117
|
+
else:
|
|
118
|
+
# Preserve the original native Passive Mode behaviour:
|
|
119
|
+
# modulate the room valve while another room is heating.
|
|
120
|
+
target_temp = min(
|
|
121
|
+
round(
|
|
122
|
+
(
|
|
123
|
+
room.current_temperature
|
|
124
|
+
+ passive_mode_increment
|
|
125
|
+
)
|
|
126
|
+
* 2
|
|
127
|
+
)
|
|
128
|
+
/ 2,
|
|
129
|
+
room.passive_mode_upper_temp,
|
|
102
130
|
)
|
|
103
|
-
/ 2,
|
|
104
|
-
(
|
|
105
|
-
room.schedule.current_setting
|
|
106
|
-
if room.mode == WiserHeatingModeEnum.auto.value
|
|
107
|
-
and room.schedule
|
|
108
|
-
else room.passive_mode_upper_temp
|
|
109
|
-
),
|
|
110
|
-
)
|
|
111
131
|
|
|
112
132
|
if target_temp < room.passive_mode_lower_temp:
|
|
113
133
|
target_temp = room.passive_mode_lower_temp
|
|
@@ -20,14 +20,16 @@ class _WiserBattery(object):
|
|
|
20
20
|
@property
|
|
21
21
|
def level(self) -> str:
|
|
22
22
|
"""Get the descritpion of the battery level"""
|
|
23
|
-
|
|
23
|
+
# Some button panels omit BatteryLevel or explicitly report null.
|
|
24
|
+
# Normalise both cases so callers can safely treat this as text.
|
|
25
|
+
return self._data.get("BatteryLevel") or TEXT_UNKNOWN
|
|
24
26
|
|
|
25
27
|
@property
|
|
26
28
|
def percent(self) -> int:
|
|
27
29
|
"""Get the percent of battery remaining"""
|
|
30
|
+
levels = {"normal": 100, "twothirds": 66, "onethird": 33, "low": 10}
|
|
28
31
|
if self.voltage is None:
|
|
29
32
|
# This device does not provide battery voltage. Calc % from level text
|
|
30
|
-
levels = {"normal": 100, "twothirds": 66, "onethird": 33, "low": 10}
|
|
31
33
|
return levels.get(self.level.lower(), 0)
|
|
32
34
|
|
|
33
35
|
elif self._data.get("ProductType") == "RoomStat" and self.voltage:
|
|
@@ -46,6 +48,10 @@ class _WiserBattery(object):
|
|
|
46
48
|
if self.voltage < TRV_FULL_BATTERY_LEVEL
|
|
47
49
|
else 100
|
|
48
50
|
)
|
|
51
|
+
elif self._data.get("ProductType") == "ButtonPanel":
|
|
52
|
+
# The hub reports a voltage for button panels but does not expose a
|
|
53
|
+
# calibrated voltage curve, so use its qualitative battery level.
|
|
54
|
+
return levels.get(self.level.lower(), 0)
|
|
49
55
|
else:
|
|
50
56
|
return None
|
|
51
57
|
|
|
@@ -218,6 +218,12 @@ class _WiserOpentherm(object):
|
|
|
218
218
|
"""Get ch2FlowSetpoint"""
|
|
219
219
|
return tf._from_wiser_temp(self._data.get("ch2FlowSetpoint", None), "current")
|
|
220
220
|
|
|
221
|
+
@property
|
|
222
|
+
def central_heating_active(self) -> bool:
|
|
223
|
+
"""Return whether the boiler is operating in central-heating mode."""
|
|
224
|
+
slave_status = self.operational_data.slave_status
|
|
225
|
+
return isinstance(slave_status, int) and bool(slave_status & (1 << 1))
|
|
226
|
+
|
|
221
227
|
@property
|
|
222
228
|
def connection_status(self) -> str:
|
|
223
229
|
"""Get opentherm connection status"""
|
|
@@ -6,7 +6,7 @@ import json
|
|
|
6
6
|
import re
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from typing import Optional
|
|
9
|
+
from typing import Callable, Optional
|
|
10
10
|
|
|
11
11
|
import aiohttp
|
|
12
12
|
import aiohttp.client_exceptions
|
|
@@ -35,7 +35,10 @@ from .helpers.extra_config import _WiserExtraConfig
|
|
|
35
35
|
class WiserAPIParams:
|
|
36
36
|
"""Class to hold default values"""
|
|
37
37
|
|
|
38
|
-
passive_mode_increment:
|
|
38
|
+
passive_mode_increment: float = 0.5
|
|
39
|
+
passive_mode_temperature_limiter_provider: Optional[Callable] = None
|
|
40
|
+
passive_mode_lower_temp_provider: Optional[Callable] = None
|
|
41
|
+
passive_mode_upper_temp_provider: Optional[Callable] = None
|
|
39
42
|
stored_manual_target_temperature_alt_source: str = "current"
|
|
40
43
|
boost_temp_delta: int = DEFAULT_BOOST_DELTA
|
|
41
44
|
hw_climate_mode: bool = False
|
|
@@ -35,12 +35,14 @@ class _WiserRoom(object):
|
|
|
35
35
|
schedule: _WiserSchedule,
|
|
36
36
|
devices: list,
|
|
37
37
|
enable_automations: bool,
|
|
38
|
+
opentherm=None,
|
|
38
39
|
):
|
|
39
40
|
self._wiser_rest_controller = wiser_rest_controller
|
|
40
41
|
self._data = room
|
|
41
42
|
self._schedule = schedule
|
|
42
43
|
self._devices = devices
|
|
43
44
|
self._enable_automations = enable_automations
|
|
45
|
+
self._opentherm = opentherm
|
|
44
46
|
self._extra_config = (
|
|
45
47
|
self._wiser_rest_controller._extra_config.config(
|
|
46
48
|
"Rooms", str(self.id)
|
|
@@ -148,6 +150,25 @@ class _WiserRoom(object):
|
|
|
148
150
|
|
|
149
151
|
@property
|
|
150
152
|
def passive_mode_lower_temp(self) -> float:
|
|
153
|
+
provider = (
|
|
154
|
+
self._wiser_rest_controller._api_parameters.passive_mode_lower_temp_provider
|
|
155
|
+
)
|
|
156
|
+
if (
|
|
157
|
+
provider is not None
|
|
158
|
+
and self.is_passive_mode
|
|
159
|
+
and self.mode == WiserHeatingModeEnum.auto.value
|
|
160
|
+
and self.schedule
|
|
161
|
+
):
|
|
162
|
+
try:
|
|
163
|
+
scheduled_lower = provider(self)
|
|
164
|
+
if scheduled_lower is not None:
|
|
165
|
+
return min(float(scheduled_lower), self.passive_mode_upper_temp)
|
|
166
|
+
except Exception:
|
|
167
|
+
_LOGGER.warning(
|
|
168
|
+
"Ignoring invalid scheduled Passive Mode lower temperature for %s",
|
|
169
|
+
self.name,
|
|
170
|
+
exc_info=True,
|
|
171
|
+
)
|
|
151
172
|
if self._extra_config and "min" in self._extra_config:
|
|
152
173
|
return min(self._extra_config["min"], self.passive_mode_upper_temp)
|
|
153
174
|
return min(
|
|
@@ -156,23 +177,46 @@ class _WiserRoom(object):
|
|
|
156
177
|
|
|
157
178
|
@property
|
|
158
179
|
def passive_mode_upper_temp(self) -> float:
|
|
180
|
+
provider = (
|
|
181
|
+
self._wiser_rest_controller._api_parameters.passive_mode_upper_temp_provider
|
|
182
|
+
)
|
|
159
183
|
if (
|
|
160
184
|
self.is_passive_mode
|
|
161
185
|
and self.mode == WiserHeatingModeEnum.auto.value
|
|
162
186
|
and self.schedule
|
|
163
187
|
):
|
|
188
|
+
if provider is not None:
|
|
189
|
+
try:
|
|
190
|
+
override_upper = provider(self)
|
|
191
|
+
if override_upper is not None:
|
|
192
|
+
return float(override_upper)
|
|
193
|
+
except Exception:
|
|
194
|
+
_LOGGER.warning(
|
|
195
|
+
"Ignoring invalid Passive Mode upper temperature override for %s",
|
|
196
|
+
self.name,
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
164
199
|
return self.schedule.current_setting
|
|
165
200
|
else:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
201
|
+
return self.configured_passive_mode_upper_temp
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def configured_passive_mode_upper_temp(self) -> float:
|
|
205
|
+
"""Return the static Passive upper temperature from extra config."""
|
|
206
|
+
if self._extra_config and "max" in self._extra_config:
|
|
207
|
+
return self._extra_config["max"]
|
|
208
|
+
return self._default_extra_config["max"]
|
|
169
209
|
|
|
170
210
|
async def set_passive_mode(self, enable: bool):
|
|
171
211
|
if await self._update_extra_config("passive_mode", enable):
|
|
172
212
|
await self._get_extra_config()
|
|
173
213
|
if not self.is_away_mode:
|
|
174
214
|
# Set to min of temp range when initialised
|
|
175
|
-
if
|
|
215
|
+
if (
|
|
216
|
+
enable
|
|
217
|
+
and self.mode != WiserHeatingModeEnum.off.value
|
|
218
|
+
and not self._auto_uses_scheduled_passive_lower_temp
|
|
219
|
+
):
|
|
176
220
|
await self.set_target_temperature(
|
|
177
221
|
self.passive_mode_lower_temp
|
|
178
222
|
)
|
|
@@ -189,6 +233,27 @@ class _WiserRoom(object):
|
|
|
189
233
|
"Unable to set passive mode. This maybe caused by an issue with your extra config file."
|
|
190
234
|
)
|
|
191
235
|
|
|
236
|
+
@property
|
|
237
|
+
def _auto_uses_scheduled_passive_lower_temp(self) -> bool:
|
|
238
|
+
"""Return whether an external limiter owns the Auto schedule target."""
|
|
239
|
+
provider = (
|
|
240
|
+
self._wiser_rest_controller._api_parameters.passive_mode_temperature_limiter_provider
|
|
241
|
+
)
|
|
242
|
+
if (
|
|
243
|
+
self.mode == WiserHeatingModeEnum.auto.value
|
|
244
|
+
and self.schedule is not None
|
|
245
|
+
and provider is not None
|
|
246
|
+
):
|
|
247
|
+
try:
|
|
248
|
+
return bool(provider(self))
|
|
249
|
+
except Exception:
|
|
250
|
+
_LOGGER.warning(
|
|
251
|
+
"Ignoring invalid Passive Mode temperature limiter for %s",
|
|
252
|
+
self.name,
|
|
253
|
+
exc_info=True,
|
|
254
|
+
)
|
|
255
|
+
return False
|
|
256
|
+
|
|
192
257
|
async def set_passive_mode_lower_temp(self, temp):
|
|
193
258
|
if not await self._update_extra_config("min", temp):
|
|
194
259
|
_LOGGER.error(
|
|
@@ -211,8 +276,9 @@ class _WiserRoom(object):
|
|
|
211
276
|
@property
|
|
212
277
|
def available_presets(self) -> list:
|
|
213
278
|
"""Get available preset modes"""
|
|
214
|
-
#
|
|
215
|
-
|
|
279
|
+
# Advance is available whenever the room has a schedule. Passive Mode
|
|
280
|
+
# advances its paired lower and upper range through the integration.
|
|
281
|
+
if not self.schedule:
|
|
216
282
|
return [
|
|
217
283
|
mode.value
|
|
218
284
|
for mode in WiserPresetOptionsEnum
|
|
@@ -363,14 +429,22 @@ class _WiserRoom(object):
|
|
|
363
429
|
else False
|
|
364
430
|
)
|
|
365
431
|
|
|
432
|
+
@property
|
|
433
|
+
def is_calling_for_heat(self) -> bool | None:
|
|
434
|
+
"""Return whether the room is requesting heat."""
|
|
435
|
+
if "PercentageDemand" not in self._data:
|
|
436
|
+
return None
|
|
437
|
+
return self.percentage_demand > 0
|
|
438
|
+
|
|
366
439
|
@property
|
|
367
440
|
def is_heating(self) -> bool:
|
|
368
|
-
"""
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
441
|
+
"""Return whether central heating is currently servicing room demand."""
|
|
442
|
+
if self._opentherm and self._opentherm.connection_status == "Connected":
|
|
443
|
+
return (
|
|
444
|
+
self.is_calling_for_heat is True
|
|
445
|
+
and self._opentherm.central_heating_active
|
|
446
|
+
)
|
|
447
|
+
return self._data.get("ControlOutputState", TEXT_OFF) == TEXT_ON
|
|
374
448
|
|
|
375
449
|
@property
|
|
376
450
|
def manual_target_temperature(self) -> float:
|
|
@@ -404,9 +478,7 @@ class _WiserRoom(object):
|
|
|
404
478
|
self.stored_manual_target_temperature
|
|
405
479
|
)
|
|
406
480
|
elif mode == WiserHeatingModeEnum.auto.value:
|
|
407
|
-
await self._send_command(
|
|
408
|
-
{"Mode": WiserHeatingModeEnum.auto.value}
|
|
409
|
-
)
|
|
481
|
+
await self._send_command({"Mode": WiserHeatingModeEnum.auto.value})
|
|
410
482
|
|
|
411
483
|
self._mode = mode
|
|
412
484
|
return True
|
|
@@ -741,6 +813,7 @@ class _WiserRoomCollection:
|
|
|
741
813
|
schedules: _WiserScheduleCollection,
|
|
742
814
|
devices: _WiserDeviceCollection,
|
|
743
815
|
enable_automations: bool,
|
|
816
|
+
opentherm=None,
|
|
744
817
|
):
|
|
745
818
|
super().__init__()
|
|
746
819
|
self._wiser_rest_controller = wiser_rest_controller
|
|
@@ -748,6 +821,7 @@ class _WiserRoomCollection:
|
|
|
748
821
|
self._schedules = schedules
|
|
749
822
|
self._devices = devices
|
|
750
823
|
self._enable_automations = enable_automations
|
|
824
|
+
self._opentherm = opentherm
|
|
751
825
|
self._rooms: list(_WiserRoom) = []
|
|
752
826
|
self._build()
|
|
753
827
|
|
|
@@ -767,6 +841,7 @@ class _WiserRoomCollection:
|
|
|
767
841
|
schedule[0] if len(schedule) > 0 else None,
|
|
768
842
|
devices,
|
|
769
843
|
self._enable_automations,
|
|
844
|
+
self._opentherm,
|
|
770
845
|
)
|
|
771
846
|
)
|
|
772
847
|
|
|
@@ -172,7 +172,13 @@ class WiserAPI:
|
|
|
172
172
|
"ActiveSystemVersion", "1.0.0"
|
|
173
173
|
)
|
|
174
174
|
)
|
|
175
|
+
_LOGGER.debug(
|
|
176
|
+
"Hub Hardware Generation: %s, Firmware Version: %s",
|
|
177
|
+
hw_gen,
|
|
178
|
+
fw_version,
|
|
179
|
+
)
|
|
175
180
|
if hw_gen == 2 and fw_version >= HUB_GEN2_MIN_HTTPS_VERSION:
|
|
181
|
+
_LOGGER.debug("Using HTTPS for Wiser Hub REST API calls")
|
|
176
182
|
self._wiser_rest_controller.use_https = True
|
|
177
183
|
|
|
178
184
|
self._network_data = await self._wiser_rest_controller.get_hub_data(
|
|
@@ -275,6 +281,7 @@ class WiserAPI:
|
|
|
275
281
|
self._schedules.get_by_type(WiserScheduleTypeEnum.heating),
|
|
276
282
|
self._devices,
|
|
277
283
|
self._enable_automations,
|
|
284
|
+
self._system.opentherm,
|
|
278
285
|
)
|
|
279
286
|
|
|
280
287
|
# Hot Water
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioWiserHeatAPI
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: An AsyncIO API for controlling the Drayton Wiser Heating system
|
|
5
5
|
Home-page: https://github.com/msp1974/aioWiserHeatAPI
|
|
6
6
|
Author: Mark Parker
|
|
7
7
|
Author-email: msparker@sky.com
|
|
8
8
|
Classifier: Development Status :: 5 - Production/Stable
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Requires-Python: >=3.12
|
|
@@ -28,7 +28,7 @@ Dynamic: requires-dist
|
|
|
28
28
|
Dynamic: requires-python
|
|
29
29
|
Dynamic: summary
|
|
30
30
|
|
|
31
|
-
# Drayton Wiser Hub API Async v1.7.
|
|
31
|
+
# Drayton Wiser Hub API Async v1.7.4
|
|
32
32
|
|
|
33
33
|
This repository contains a simple API which queries the Drayton Wiser Heating sysystem used in the UK.
|
|
34
34
|
|
|
@@ -37,7 +37,7 @@ The API also supports Smart Plugs and initial basic functionality for Shutter an
|
|
|
37
37
|
|
|
38
38
|
## Requirements
|
|
39
39
|
|
|
40
|
-
Requires a minimum of Python v3.
|
|
40
|
+
Requires a minimum of Python v3.12
|
|
41
41
|
|
|
42
42
|
## Installation
|
|
43
43
|
|
|
@@ -82,9 +82,17 @@ Documentation available in [info.md](https://github.com/msp1974/wiserHeatAPIv2/b
|
|
|
82
82
|
|
|
83
83
|
## Changelog
|
|
84
84
|
|
|
85
|
-
### v1.7.
|
|
85
|
+
### v1.7.4
|
|
86
86
|
|
|
87
|
-
-
|
|
87
|
+
- Added: optional scheduled lower-temperature provider for Passive Mode
|
|
88
|
+
- Added: EVSE device support
|
|
89
|
+
- Added: Button panel battery status
|
|
90
|
+
- Added: Support for the `OffAfter2s` LED indicator mode
|
|
91
|
+
- Fix: Refine room heating state for OpenTherm systems
|
|
92
|
+
|
|
93
|
+
### v1.7.3
|
|
94
|
+
|
|
95
|
+
- Fix: Set min f/w version of 4.42.22 for https on gen2 hubs
|
|
88
96
|
|
|
89
97
|
### v1.7.0
|
|
90
98
|
|
|
@@ -35,7 +35,7 @@ setuptools.setup(
|
|
|
35
35
|
classifiers=[
|
|
36
36
|
"Development Status :: 5 - Production/Stable",
|
|
37
37
|
"Intended Audience :: Developers",
|
|
38
|
-
"Programming Language :: Python :: 3.
|
|
38
|
+
"Programming Language :: Python :: 3.12",
|
|
39
39
|
"License :: OSI Approved :: MIT License",
|
|
40
40
|
"Operating System :: OS Independent",
|
|
41
41
|
],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aiowiserheatapi-1.7.2 → aiowiserheatapi-1.7.4}/aioWiserHeatAPI.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|