aioWiserHeatAPI 1.5.19__tar.gz → 1.6.1__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.
Files changed (62) hide show
  1. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/PKG-INFO +15 -2
  2. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/README.md +14 -1
  3. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/__init__.py +1 -1
  4. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/binary_sensor.py +40 -0
  5. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/const.py +3 -0
  6. aiowiserheatapi-1.6.1/aioWiserHeatAPI/devices.py +465 -0
  7. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/heating_actuator.py +18 -0
  8. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/device.py +10 -2
  9. aiowiserheatapi-1.6.1/aioWiserHeatAPI/helpers/threshold.py +93 -0
  10. aiowiserheatapi-1.6.1/aioWiserHeatAPI/helpers/uiconfiguration.py +73 -0
  11. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/hot_water.py +114 -35
  12. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/light.py +24 -3
  13. aiowiserheatapi-1.6.1/aioWiserHeatAPI/ptc.py +192 -0
  14. aiowiserheatapi-1.6.1/aioWiserHeatAPI/temp_humidity.py +50 -0
  15. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/PKG-INFO +15 -2
  16. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/SOURCES.txt +4 -0
  17. aiowiserheatapi-1.5.19/aioWiserHeatAPI/devices.py +0 -373
  18. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/LICENSE +0 -0
  19. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/automation.py +0 -0
  20. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/boiler_interface.py +0 -0
  21. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/button_panel.py +0 -0
  22. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/cli.py +0 -0
  23. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/discovery.py +0 -0
  24. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/exceptions.py +0 -0
  25. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/heating.py +0 -0
  26. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/__init__.py +0 -0
  27. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/automations.py +0 -0
  28. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/battery.py +0 -0
  29. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/capabilities.py +0 -0
  30. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/cloud.py +0 -0
  31. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/equipment.py +0 -0
  32. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/extra_config.py +0 -0
  33. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/firmware.py +0 -0
  34. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/gps.py +0 -0
  35. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/misc.py +0 -0
  36. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/network.py +0 -0
  37. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/opentherm.py +0 -0
  38. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/signal.py +0 -0
  39. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/special_times.py +0 -0
  40. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/status.py +0 -0
  41. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/temp.py +0 -0
  42. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/version.py +0 -0
  43. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/helpers/zigbee.py +0 -0
  44. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/moments.py +0 -0
  45. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/pte.py +0 -0
  46. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/rest_controller.py +0 -0
  47. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/room.py +0 -0
  48. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/roomstat.py +0 -0
  49. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/schedule.py +0 -0
  50. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/shutter.py +0 -0
  51. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/smartplug.py +0 -0
  52. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/smartvalve.py +0 -0
  53. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/smokealarm.py +0 -0
  54. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/system.py +0 -0
  55. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/ufh.py +0 -0
  56. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI/wiserhub.py +0 -0
  57. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/dependency_links.txt +0 -0
  58. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/entry_points.txt +0 -0
  59. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/requires.txt +0 -0
  60. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/aioWiserHeatAPI.egg-info/top_level.txt +0 -0
  61. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/setup.cfg +0 -0
  62. {aiowiserheatapi-1.5.19 → aiowiserheatapi-1.6.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioWiserHeatAPI
3
- Version: 1.5.19
3
+ Version: 1.6.1
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
@@ -18,7 +18,7 @@ Requires-Dist: aiohttp>=3.9.3
18
18
  Requires-Dist: aiofiles>=23.2.1
19
19
  Requires-Dist: pyyaml>=6.0.1
20
20
 
21
- # Drayton Wiser Hub API Async v1.5.19
21
+ # Drayton Wiser Hub API Async v1.6.1
22
22
 
23
23
  This repository contains a simple API which queries the Drayton Wiser Heating sysystem used in the UK.
24
24
 
@@ -72,6 +72,19 @@ Documentation available in [info.md](https://github.com/msp1974/wiserHeatAPIv2/b
72
72
 
73
73
  ## Changelog
74
74
 
75
+ ### v1.6.1
76
+ * Added: support for temperature and humidity sensors
77
+
78
+ ### v1.6.0
79
+ * Refactored how devices are enumerated
80
+ * BREAKING CHANGE - threshold sensor is now a device attribute and not its own device
81
+ * Added: support for 2 gang light switches
82
+ * Added: support for threshold and UI configuration as device attributes
83
+ * Added: support for PTC devices
84
+ * Added: UUID now exists for all devices (for future use)
85
+ * Added: New attributes and stored extra config for Hot Water to support HW Climate automation in HA integration
86
+ * Added: Control of interacts with room climate for binary sensors
87
+
75
88
  ### v1.5.19
76
89
  * Fixed error when BoilerInterface does not provide DeviceId field
77
90
  * Fixed not supporting non ascii characters in schedule names
@@ -1,4 +1,4 @@
1
- # Drayton Wiser Hub API Async v1.5.19
1
+ # Drayton Wiser Hub API Async v1.6.1
2
2
 
3
3
  This repository contains a simple API which queries the Drayton Wiser Heating sysystem used in the UK.
4
4
 
@@ -52,6 +52,19 @@ Documentation available in [info.md](https://github.com/msp1974/wiserHeatAPIv2/b
52
52
 
53
53
  ## Changelog
54
54
 
55
+ ### v1.6.1
56
+ * Added: support for temperature and humidity sensors
57
+
58
+ ### v1.6.0
59
+ * Refactored how devices are enumerated
60
+ * BREAKING CHANGE - threshold sensor is now a device attribute and not its own device
61
+ * Added: support for 2 gang light switches
62
+ * Added: support for threshold and UI configuration as device attributes
63
+ * Added: support for PTC devices
64
+ * Added: UUID now exists for all devices (for future use)
65
+ * Added: New attributes and stored extra config for Hot Water to support HW Climate automation in HA integration
66
+ * Added: Control of interacts with room climate for binary sensors
67
+
55
68
  ### v1.5.19
56
69
  * Fixed error when BoilerInterface does not provide DeviceId field
57
70
  * Fixed not supporting non ascii characters in schedule names
@@ -3,6 +3,6 @@ import logging
3
3
  name = "aioWiserHeatAPI"
4
4
  __all__ = ["wiserAPI", "wiserDiscovery"]
5
5
 
6
- __VERSION__ = "1.5.19"
6
+ __VERSION__ = "1.6.1"
7
7
 
8
8
  _LOGGER = logging.getLogger(__name__)
@@ -2,13 +2,43 @@
2
2
  Handles binary_sensor devices
3
3
  """
4
4
 
5
+ import inspect
6
+
7
+ from aioWiserHeatAPI import _LOGGER
8
+
5
9
  from .helpers.battery import _WiserBattery
6
10
  from .helpers.device import _WiserDevice
11
+ from .helpers.threshold import _WiserThresholdSensor
7
12
 
8
13
 
9
14
  class _WiserBinarySensor(_WiserDevice):
10
15
  """Class representing a Wiser Binary Sensor"""
11
16
 
17
+ def __init__(self, *args):
18
+ """Initialise."""
19
+ super().__init__(*args)
20
+ self._threshold_sensors: list[_WiserThresholdSensor] = []
21
+
22
+ async def _send_command(self, cmd: dict):
23
+ """
24
+ Send control command to the Threshold sensor
25
+ param cmd: json command structure
26
+ return: boolen - true = success, false = failed
27
+ """
28
+ result = await self._wiser_rest_controller._send_command(
29
+ self._endpoint.format(self.device_type_id), cmd
30
+ )
31
+ if result:
32
+ self._device_type_data = result
33
+ if result:
34
+ _LOGGER.debug(
35
+ "Wiser light - {} command successful".format(
36
+ inspect.stack()[1].function
37
+ )
38
+ )
39
+ return True
40
+ return False
41
+
12
42
  @property
13
43
  def active(self) -> bool:
14
44
  """Get if is active"""
@@ -19,6 +49,11 @@ class _WiserBinarySensor(_WiserDevice):
19
49
  """Get the if interacts with room climate"""
20
50
  return self._device_type_data.get("InteractsWithRoomClimate")
21
51
 
52
+ async def set_interacts_with_room_climate(self, enabled: bool):
53
+ if await self._send_command({"InteractsWithRoomClimate": str(enabled).lower()}):
54
+ self._interacts_with_room_climate = enabled
55
+ return True
56
+
22
57
  @property
23
58
  def type(self) -> str:
24
59
  """Get the type of device"""
@@ -34,6 +69,11 @@ class _WiserBinarySensor(_WiserDevice):
34
69
  """Get the battery information for the smokealarm"""
35
70
  return _WiserBattery(self._data)
36
71
 
72
+ @property
73
+ def threshold_sensors(self) -> list[_WiserThresholdSensor]:
74
+ """Get threshold sensors."""
75
+ return self._threshold_sensors
76
+
37
77
 
38
78
  class _WiserWindowDoorSensor(_WiserBinarySensor):
39
79
  """Class representing a Wiser WindowDoor Sensor"""
@@ -101,11 +101,14 @@ WISERHEATINGACTUATOR = "HeatingActuator/{}"
101
101
  WISERUFHCONTROLLER = "UnderFloorHeating/{}"
102
102
  WISERSHUTTER = "Shutter/{}"
103
103
  WISERLIGHT = "Light/{}"
104
+ WISERPOWERTAGCONTROL = "PTC/{}"
104
105
  WISERPOWERTAGENERGY = "PTE/{}"
105
106
  WISERSMOKEALARM = "SmokeAlarmDevice/{}"
106
107
  WISERBINARYSENSOR = "BinarySensor/{}"
107
108
  WISERBOILERINTERFACE = "BoilerInterface/{}"
108
109
  WISERBUTTONPANEL = "ButtonPanel/{}"
110
+ WISERTHRESHOLDSENSOR = "ThresholdSensor/{}"
111
+ WISERUICONFIGURATION = "UIConfiguration/{}"
109
112
 
110
113
 
111
114
  # Enums
@@ -0,0 +1,465 @@
1
+ """
2
+ Module to manage all devices
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any
7
+
8
+ from .binary_sensor import (
9
+ _WiserBinarySensor,
10
+ _WiserBinarySensorCollection,
11
+ )
12
+ from .boiler_interface import _WiserBoilerInterface, _WiserBoilerInterfaceCollection
13
+ from .button_panel import _WiserButtonPanel, _WiserButtonPanelCollection
14
+ from .const import (
15
+ WISERBINARYSENSOR,
16
+ WISERBOILERINTERFACE,
17
+ WISERBUTTONPANEL,
18
+ WISERHEATINGACTUATOR,
19
+ WISERLIGHT,
20
+ WISERPOWERTAGCONTROL,
21
+ WISERPOWERTAGENERGY,
22
+ WISERROOMSTAT,
23
+ WISERSHUTTER,
24
+ WISERSMARTPLUG,
25
+ WISERSMARTVALVE,
26
+ WISERSMOKEALARM,
27
+ WISERTHRESHOLDSENSOR,
28
+ WISERUFHCONTROLLER,
29
+ WISERUICONFIGURATION,
30
+ )
31
+ from .heating_actuator import _WiserHeatingActuator, _WiserHeatingActuatorCollection
32
+ from .helpers.device import _WiserDevice
33
+ from .helpers.threshold import (
34
+ _WiserThresholdSensor,
35
+ )
36
+ from .helpers.uiconfiguration import _WiserUIConfigSensor
37
+ from .light import _WiserDimmableLight, _WiserLight, _WiserLightCollection
38
+ from .ptc import _WiserPowerTagControl, _WiserPowerTagControlCollection
39
+ from .pte import _WiserPowerTagEnergy, _WiserPowerTagEnergyCollection
40
+ from .rest_controller import _WiserRestController
41
+ from .roomstat import _WiserRoomStat, _WiserRoomStatCollection
42
+ from .schedule import WiserScheduleTypeEnum, _WiserScheduleCollection
43
+ from .shutter import _WiserShutter, _WiserShutterCollection
44
+ from .smartplug import _WiserSmartPlug, _WiserSmartPlugCollection
45
+ from .smartvalve import _WiserSmartValve, _WiserSmartValveCollection
46
+ from .smokealarm import _WiserSmokeAlarm, _WiserSmokeAlarmCollection
47
+ from .temp_humidity import _WiserTempHumidity, _WiserTempHumidityCollection
48
+ from .ufh import _WiserUFHController, _WiserUFHControllerCollection
49
+
50
+
51
+ @dataclass(frozen=True, kw_only=True)
52
+ class DeviceConfig:
53
+ device_class: Any
54
+ collection: Any
55
+ endpoint: str = None
56
+ heating: bool = False
57
+ schedule_type: WiserScheduleTypeEnum | None = None
58
+ has_v2_equipment: bool = False
59
+
60
+
61
+ @dataclass(frozen=True, kw_only=True)
62
+ class AncillaryDeviceConfig:
63
+ device_class: Any
64
+ attribute: str
65
+ endpoint: str = None
66
+
67
+
68
+ PRODUCT_TYPE_CONFIG = {
69
+ "SmartValve": DeviceConfig(
70
+ device_class=_WiserSmartValve,
71
+ collection=_WiserSmartValveCollection,
72
+ endpoint=WISERSMARTVALVE,
73
+ heating=True,
74
+ ),
75
+ "RoomStat": DeviceConfig(
76
+ device_class=_WiserRoomStat,
77
+ collection=_WiserRoomStatCollection,
78
+ endpoint=WISERROOMSTAT,
79
+ heating=True,
80
+ ),
81
+ "SmartPlug": DeviceConfig(
82
+ device_class=_WiserSmartPlug,
83
+ collection=_WiserSmartPlugCollection,
84
+ endpoint=WISERSMARTPLUG,
85
+ schedule_type=WiserScheduleTypeEnum.onoff,
86
+ has_v2_equipment=True,
87
+ ),
88
+ "HeatingActuator": DeviceConfig(
89
+ device_class=_WiserHeatingActuator,
90
+ collection=_WiserHeatingActuatorCollection,
91
+ endpoint=WISERHEATINGACTUATOR,
92
+ heating=True,
93
+ has_v2_equipment=True,
94
+ ),
95
+ "UnderFloorHeating": DeviceConfig(
96
+ device_class=_WiserUFHController,
97
+ collection=_WiserUFHControllerCollection,
98
+ endpoint=WISERUFHCONTROLLER,
99
+ heating=True,
100
+ ),
101
+ "Light": DeviceConfig(
102
+ device_class=_WiserLight,
103
+ collection=_WiserLightCollection,
104
+ endpoint=WISERLIGHT,
105
+ schedule_type=WiserScheduleTypeEnum.level,
106
+ ),
107
+ "Shutter": DeviceConfig(
108
+ device_class=_WiserShutter,
109
+ collection=_WiserShutterCollection,
110
+ endpoint=WISERSHUTTER,
111
+ schedule_type=WiserScheduleTypeEnum.level,
112
+ ),
113
+ "PTC": DeviceConfig(
114
+ device_class=_WiserPowerTagControl,
115
+ collection=_WiserPowerTagControlCollection,
116
+ endpoint=WISERPOWERTAGCONTROL,
117
+ has_v2_equipment=True,
118
+ ),
119
+ "PTE": DeviceConfig(
120
+ device_class=_WiserPowerTagEnergy,
121
+ collection=_WiserPowerTagEnergyCollection,
122
+ endpoint=WISERPOWERTAGENERGY,
123
+ has_v2_equipment=True,
124
+ ),
125
+ "SmokeAlarmDevice": DeviceConfig(
126
+ device_class=_WiserSmokeAlarm,
127
+ collection=_WiserSmokeAlarmCollection,
128
+ endpoint=WISERSMOKEALARM,
129
+ ),
130
+ "BinarySensor": DeviceConfig(
131
+ device_class=_WiserBinarySensor,
132
+ collection=_WiserBinarySensorCollection,
133
+ endpoint=WISERBINARYSENSOR,
134
+ ),
135
+ "BoilerInterface": DeviceConfig(
136
+ device_class=_WiserBoilerInterface,
137
+ collection=_WiserBoilerInterfaceCollection,
138
+ endpoint=WISERBOILERINTERFACE,
139
+ ),
140
+ "ButtonPanel": DeviceConfig(
141
+ device_class=_WiserButtonPanel,
142
+ collection=_WiserButtonPanelCollection,
143
+ endpoint=WISERBUTTONPANEL,
144
+ ),
145
+ "TempHumidity": DeviceConfig(
146
+ device_class=_WiserTempHumidity,
147
+ collection=_WiserTempHumidityCollection,
148
+ endpoint=None,
149
+ ),
150
+ }
151
+
152
+ ANCILLARY_SENSOR_CONFIG = {
153
+ "ThresholdSensor": AncillaryDeviceConfig(
154
+ device_class=_WiserThresholdSensor,
155
+ attribute="_threshold_sensors",
156
+ endpoint=WISERTHRESHOLDSENSOR,
157
+ ),
158
+ "UIConfiguration": AncillaryDeviceConfig(
159
+ device_class=_WiserUIConfigSensor,
160
+ attribute="_uiconfig_sensors",
161
+ endpoint=WISERUICONFIGURATION,
162
+ ),
163
+ }
164
+
165
+
166
+ class _WiserDeviceCollection:
167
+ """Class holding all wiser devices"""
168
+
169
+ def __init__(
170
+ self,
171
+ wiser_rest_controller: _WiserRestController,
172
+ domain_data: dict,
173
+ schedules: _WiserScheduleCollection,
174
+ ):
175
+ self._wiser_rest_controller = wiser_rest_controller
176
+ self._device_data = domain_data.get("Device", {})
177
+ self._domain_data = domain_data
178
+ self._equipment_data = domain_data.get("Equipment", {}) # Supported by v2 hub
179
+ self._schedules = schedules
180
+ self._device_collection = {}
181
+
182
+ self._build()
183
+
184
+ def _get_equipment_data(self, equipment_id: int) -> dict:
185
+ """Get equipment data"""
186
+ equipment_data = [
187
+ equipment_data
188
+ for equipment_data in self._equipment_data
189
+ if equipment_data.get("id") == equipment_id
190
+ ]
191
+ return equipment_data[0] if equipment_data else None
192
+
193
+ def _get_device_info_by_id(self, device_id: int) -> dict[str, Any] | None:
194
+ """Get device entry by id."""
195
+ for device in self._domain_data.get("Device"):
196
+ if device.get("id") == device_id:
197
+ return device
198
+ return None
199
+
200
+ def _build(self):
201
+ """Updated builf collection of devices.
202
+
203
+ Starts with device type key first and then gets matching device data
204
+ """
205
+ # TODO - limit checks based on DeviceCapabilityMatrix
206
+ for device_type in PRODUCT_TYPE_CONFIG:
207
+ # If not yet in the collection, add empty collection to device collections.
208
+ if device_type not in self._device_collection:
209
+ self._device_collection[device_type] = PRODUCT_TYPE_CONFIG[
210
+ device_type
211
+ ].collection()
212
+
213
+ if device_type == "TempHumidity":
214
+ # This is a workaround for temp humidity device(s)
215
+ for device in self._domain_data.get("Device"):
216
+ if device.get("ProductType") == "TemperatureHumiditySensor":
217
+ device_config = PRODUCT_TYPE_CONFIG[device_type]
218
+ device_class = device_config.device_class
219
+ self._device_collection[device_type]._items.append(
220
+ device_class(
221
+ self._wiser_rest_controller,
222
+ device_config.endpoint,
223
+ device,
224
+ {},
225
+ None,
226
+ )
227
+ )
228
+
229
+ elif device_type_data := self._domain_data.get(device_type):
230
+ # We have this device type in data
231
+ device_config = PRODUCT_TYPE_CONFIG[device_type]
232
+
233
+ # Now build collection of devices
234
+ for device in device_type_data:
235
+ # Get class to create and collection to append for device
236
+ device_class = device_config.device_class
237
+
238
+ # TODO: Fix this so not fudged
239
+ if device_type == "Light" and device.get("IsDimmable"):
240
+ device_class = _WiserDimmableLight
241
+
242
+ # Device info id can be DeviceId or id depending on device type
243
+ device_info_id = device.get("DeviceId", device.get("id"))
244
+
245
+ # Get device info data
246
+ device_info_data = self._get_device_info_by_id(device_info_id)
247
+
248
+ # If heating device add room id
249
+ if device_config.heating:
250
+ if not device.get("RoomId", device_info_data.get("RoomId")):
251
+ device["RoomId"] = self._get_temp_device_room_id(
252
+ self._domain_data, device_info_id
253
+ )
254
+
255
+ # If schedule device add schedule
256
+ if device_config.schedule_type:
257
+ device_schedule = self._schedules.get_by_id(
258
+ device_config.schedule_type, device.get("ScheduleId")
259
+ )
260
+ else:
261
+ device_schedule = None
262
+
263
+ # Add device to collection
264
+ self._device_collection[device_type]._items.append(
265
+ device_class(
266
+ self._wiser_rest_controller,
267
+ device_config.endpoint,
268
+ device_info_data,
269
+ device,
270
+ device_schedule,
271
+ )
272
+ )
273
+
274
+ # Add ancillary sensors to device
275
+ for anc_device_type, anc_device_type_info in ANCILLARY_SENSOR_CONFIG.items():
276
+ if anc_device_type_data := self._domain_data.get(anc_device_type):
277
+ for anc_device in anc_device_type_data:
278
+ device_id = anc_device.get("DeviceId", anc_device.get("id"))
279
+ if device := self.get_by_id(device_id):
280
+ if hasattr(device, anc_device_type_info.attribute):
281
+ getattr(device, anc_device_type_info.attribute).append(
282
+ anc_device_type_info.device_class(
283
+ self._wiser_rest_controller,
284
+ anc_device_type_info.endpoint,
285
+ anc_device,
286
+ )
287
+ )
288
+
289
+ def _get_temp_device_room_id(self, domain_data: dict, device_id: int) -> int:
290
+ rooms = domain_data.get("Room")
291
+ for room in rooms:
292
+ room_device_list = []
293
+ room_device_list.extend(room.get("SmartValveIds", []))
294
+ room_device_list.extend(room.get("HeatingActuatorIds", []))
295
+ room_device_list.append(room.get("RoomStatId"))
296
+ room_device_list.append(room.get("UnderFloorHeatingId"))
297
+ if device_id in room_device_list:
298
+ return room.get("id")
299
+ return 0
300
+
301
+ @property
302
+ def all(self) -> list[_WiserDevice]:
303
+ """Return all devices"""
304
+ items = []
305
+ for key in self._device_collection:
306
+ items.extend(self._device_collection[key].all)
307
+ return items
308
+
309
+ @property
310
+ def count(self) -> int:
311
+ """Return count of devices"""
312
+ return len(self.all)
313
+
314
+ @property
315
+ def heating_actuators(self) -> _WiserHeatingActuatorCollection:
316
+ """Return all heating actuators"""
317
+ try:
318
+ return self._device_collection["HeatingActuator"]
319
+ except KeyError:
320
+ return None
321
+
322
+ @property
323
+ def lights(self) -> _WiserLightCollection:
324
+ """Return all lights"""
325
+ try:
326
+ return self._device_collection["Light"]
327
+ except KeyError:
328
+ return None
329
+
330
+ @property
331
+ def power_tags_c(self) -> _WiserPowerTagControlCollection:
332
+ """Return all power tag Cs"""
333
+ try:
334
+ return self._device_collection["PTC"]
335
+ except KeyError:
336
+ return None
337
+
338
+ @property
339
+ def power_tags(self) -> _WiserPowerTagEnergyCollection:
340
+ """Return all power tags"""
341
+ try:
342
+ return self._device_collection["PTE"]
343
+ except KeyError:
344
+ return None
345
+
346
+ @property
347
+ def roomstats(self) -> _WiserRoomStatCollection:
348
+ """Return all roomstats"""
349
+ try:
350
+ return self._device_collection["RoomStat"]
351
+ except KeyError:
352
+ return None
353
+
354
+ @property
355
+ def shutters(self) -> _WiserShutterCollection:
356
+ """Return all shutters"""
357
+ try:
358
+ return self._device_collection["Shutter"]
359
+ except KeyError:
360
+ return None
361
+
362
+ @property
363
+ def smartplugs(self) -> _WiserSmartPlugCollection:
364
+ """Return all smart plugs"""
365
+ try:
366
+ return self._device_collection["SmartPlug"]
367
+ except KeyError:
368
+ return None
369
+
370
+ @property
371
+ def smartvalves(self) -> _WiserSmartValveCollection:
372
+ """Return all smart valves (iTRVs)"""
373
+ try:
374
+ return self._device_collection["SmartValve"]
375
+ except KeyError:
376
+ return None
377
+
378
+ @property
379
+ def smokealarms(self) -> _WiserSmokeAlarmCollection:
380
+ """Return all smoke alarms"""
381
+ try:
382
+ return self._device_collection["SmokeAlarmDevice"]
383
+ except KeyError:
384
+ return None
385
+
386
+ @property
387
+ def ufh_controllers(self) -> _WiserUFHControllerCollection:
388
+ """Return all UFH controllers"""
389
+ try:
390
+ return self._device_collection["UnderFloorHeating"]
391
+ except KeyError:
392
+ return None
393
+
394
+ @property
395
+ def binary_sensor(self):
396
+ """Return all binary sensors"""
397
+ try:
398
+ return self._device_collection["BinarySensor"]
399
+ except KeyError:
400
+ return None
401
+
402
+ @property
403
+ def temp_humidity_sensors(self):
404
+ """Return all temp humidity sensors"""
405
+ try:
406
+ return self._device_collection["TempHumidity"]
407
+ except KeyError:
408
+ return None
409
+
410
+ def get_by_id(self, device_id: int) -> _WiserDevice:
411
+ """
412
+ Gets a device object from the devices id
413
+ param id: id of device
414
+ return: Device type class
415
+ """
416
+ try:
417
+ return [device for device in self.all if device.id == device_id][0]
418
+ except IndexError:
419
+ return None
420
+
421
+ def get_by_room_id(self, room_id: int) -> list[_WiserDevice]:
422
+ """
423
+ Gets a list of devices belonging to the room id
424
+ param room_id: the id of the room
425
+ return: Device type class
426
+ """
427
+ try:
428
+ return [device for device in self.all if device.room_id == room_id]
429
+ except IndexError:
430
+ return None
431
+
432
+ def get_by_node_id(self, node_id: int) -> _WiserDevice:
433
+ """
434
+ Gets a device object from the devices zigbee node id
435
+ param node_id: zigbee node id of device
436
+ return: Device type class
437
+ """
438
+ try:
439
+ return [device for device in self.all if device.node_id == node_id][0]
440
+ except IndexError:
441
+ return None
442
+
443
+ def get_by_serial_number(self, serial_number: str) -> _WiserDevice:
444
+ """
445
+ Gets a device object from the devices serial number
446
+ param node_id: serial number of device
447
+ return: Device type class
448
+ """
449
+ try:
450
+ return [
451
+ device for device in self.all if device.serial_number == serial_number
452
+ ][0]
453
+ except IndexError:
454
+ return None
455
+
456
+ def get_by_parent_node_id(self, node_id: int) -> list[_WiserDevice]:
457
+ """
458
+ Gets a list of device from the devices zigbee parent node id
459
+ param node_id: zigbee parent node id of device
460
+ return: Device type class
461
+ """
462
+ try:
463
+ return [device for device in self.all if device.parent_node_id == node_id]
464
+ except IndexError:
465
+ return None
@@ -2,6 +2,8 @@ from .const import TEMP_OFF, TEXT_UNKNOWN, WISERHEATINGACTUATOR, WiserTempLimits
2
2
  from .helpers.device import _WiserDevice
3
3
  from .helpers.equipment import _WiserEquipment
4
4
  from .helpers.temp import _WiserTemperatureFunctions as tf
5
+ from .helpers.threshold import _WiserThresholdSensor
6
+ from .helpers.uiconfiguration import _WiserUIConfigSensor
5
7
  from .rest_controller import _WiserRestController
6
8
 
7
9
 
@@ -107,6 +109,12 @@ class _WiserTemperatureSensor:
107
109
  class _WiserHeatingActuator(_WiserDevice):
108
110
  """Class representing a Wiser Heating Actuator device"""
109
111
 
112
+ def __init__(self, *args):
113
+ """Initialise."""
114
+ super().__init__(*args)
115
+ self._threshold_sensors: list[_WiserThresholdSensor] = []
116
+ self._uiconfig_sensors: list[_WiserUIConfigSensor] = []
117
+
110
118
  @property
111
119
  def current_target_temperature(self) -> float:
112
120
  """Get the current target temperature setting"""
@@ -161,6 +169,16 @@ class _WiserHeatingActuator(_WiserDevice):
161
169
  """Get output type"""
162
170
  return self._device_type_data.get("OutputType", TEXT_UNKNOWN)
163
171
 
172
+ @property
173
+ def threshold_sensors(self) -> list[_WiserThresholdSensor]:
174
+ """Get threshold sensors."""
175
+ return self._threshold_sensors
176
+
177
+ @property
178
+ def uiconfig_sensors(self) -> list[_WiserUIConfigSensor]:
179
+ """Get ui configuration sensors."""
180
+ return self._uiconfig_sensors
181
+
164
182
 
165
183
  class _WiserHeatingActuatorCollection(object):
166
184
  """Class holding all wiser heating actuators"""
@@ -1,5 +1,7 @@
1
+ import hashlib
1
2
  import inspect
2
3
  from typing import Union
4
+ from uuid import UUID
3
5
 
4
6
  from .. import _LOGGER
5
7
  from ..const import (
@@ -76,7 +78,7 @@ class _WiserDevice(object):
76
78
  @property
77
79
  def device_type_id(self) -> int:
78
80
  """Get the device id for the specific device type"""
79
- return self._data.get("id")
81
+ return self._device_type_data.get("id")
80
82
 
81
83
  @property
82
84
  def identify(self) -> bool:
@@ -188,7 +190,13 @@ class _WiserDevice(object):
188
190
  @property
189
191
  def uuid(self) -> str:
190
192
  """Get UUID zigbee"""
191
- return self._device_type_data.get("UUID", TEXT_UNKNOWN)
193
+ if uuid := self._device_type_data.get("UUID"):
194
+ return uuid
195
+ uname = (
196
+ f"{self.product_type}_{self.serial_number}_{self.id}_{self.device_type_id}"
197
+ )
198
+ hexstr = hashlib.md5(uname.encode("UTF-8")).hexdigest()
199
+ return UUID(hex=hexstr)
192
200
 
193
201
  # END Added by LGO
194
202