qolsys-controller 0.0.44__py3-none-any.whl → 0.0.87__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.

Potentially problematic release.


This version of qolsys-controller might be problematic. Click here for more details.

Files changed (81) hide show
  1. qolsys_controller/adc_device.py +202 -0
  2. qolsys_controller/adc_service.py +139 -0
  3. qolsys_controller/adc_service_garagedoor.py +35 -0
  4. qolsys_controller/controller.py +1040 -20
  5. qolsys_controller/database/db.py +108 -29
  6. qolsys_controller/database/table.py +90 -60
  7. qolsys_controller/database/table_alarmedsensor.py +2 -2
  8. qolsys_controller/database/table_automation.py +0 -1
  9. qolsys_controller/database/table_country_locale.py +0 -1
  10. qolsys_controller/database/table_dashboard_msgs.py +1 -2
  11. qolsys_controller/database/table_dimmerlight.py +0 -1
  12. qolsys_controller/database/table_doorlock.py +0 -1
  13. qolsys_controller/database/table_eu_event.py +1 -2
  14. qolsys_controller/database/table_heat_map.py +0 -2
  15. qolsys_controller/database/table_history.py +4 -1
  16. qolsys_controller/database/table_iqremotesettings.py +0 -2
  17. qolsys_controller/database/table_iqrouter_network_config.py +0 -1
  18. qolsys_controller/database/table_iqrouter_user_device.py +0 -2
  19. qolsys_controller/database/table_master_slave.py +0 -1
  20. qolsys_controller/database/table_nest_device.py +0 -1
  21. qolsys_controller/database/table_output_rules.py +0 -1
  22. qolsys_controller/database/table_partition.py +0 -1
  23. qolsys_controller/database/table_pgm_outputs.py +0 -2
  24. qolsys_controller/database/table_powerg_device.py +0 -2
  25. qolsys_controller/database/table_qolsyssettings.py +0 -2
  26. qolsys_controller/database/table_scene.py +0 -2
  27. qolsys_controller/database/table_sensor.py +2 -2
  28. qolsys_controller/database/table_sensor_group.py +23 -0
  29. qolsys_controller/database/table_shades.py +0 -2
  30. qolsys_controller/database/table_smartsocket.py +12 -3
  31. qolsys_controller/database/table_state.py +0 -1
  32. qolsys_controller/database/table_tcc.py +0 -1
  33. qolsys_controller/database/table_thermostat.py +3 -1
  34. qolsys_controller/database/table_trouble_conditions.py +0 -2
  35. qolsys_controller/database/table_user.py +0 -2
  36. qolsys_controller/database/table_virtual_device.py +13 -3
  37. qolsys_controller/database/table_weather.py +0 -2
  38. qolsys_controller/database/table_zigbee_device.py +0 -1
  39. qolsys_controller/database/table_zwave_association_group.py +0 -1
  40. qolsys_controller/database/table_zwave_history.py +0 -1
  41. qolsys_controller/database/table_zwave_node.py +3 -1
  42. qolsys_controller/database/table_zwave_other.py +0 -1
  43. qolsys_controller/enum.py +42 -13
  44. qolsys_controller/enum_adc.py +28 -0
  45. qolsys_controller/enum_zwave.py +210 -36
  46. qolsys_controller/errors.py +14 -12
  47. qolsys_controller/mdns.py +7 -4
  48. qolsys_controller/mqtt_command.py +125 -0
  49. qolsys_controller/mqtt_command_queue.py +5 -4
  50. qolsys_controller/observable.py +2 -2
  51. qolsys_controller/panel.py +304 -156
  52. qolsys_controller/partition.py +149 -127
  53. qolsys_controller/pki.py +69 -97
  54. qolsys_controller/scene.py +30 -28
  55. qolsys_controller/settings.py +96 -50
  56. qolsys_controller/state.py +221 -34
  57. qolsys_controller/task_manager.py +11 -14
  58. qolsys_controller/users.py +25 -0
  59. qolsys_controller/utils_mqtt.py +8 -16
  60. qolsys_controller/weather.py +71 -0
  61. qolsys_controller/zone.py +243 -214
  62. qolsys_controller/zwave_device.py +234 -93
  63. qolsys_controller/zwave_dimmer.py +55 -49
  64. qolsys_controller/zwave_energy_clamp.py +15 -0
  65. qolsys_controller/zwave_garagedoor.py +3 -1
  66. qolsys_controller/zwave_generic.py +5 -3
  67. qolsys_controller/zwave_lock.py +51 -44
  68. qolsys_controller/zwave_outlet.py +3 -1
  69. qolsys_controller/zwave_service_meter.py +192 -0
  70. qolsys_controller/zwave_service_multilevelsensor.py +119 -0
  71. qolsys_controller/zwave_thermometer.py +21 -0
  72. qolsys_controller/zwave_thermostat.py +249 -143
  73. qolsys_controller-0.0.87.dist-info/METADATA +89 -0
  74. qolsys_controller-0.0.87.dist-info/RECORD +77 -0
  75. {qolsys_controller-0.0.44.dist-info → qolsys_controller-0.0.87.dist-info}/WHEEL +1 -1
  76. qolsys_controller/plugin.py +0 -34
  77. qolsys_controller/plugin_c4.py +0 -17
  78. qolsys_controller/plugin_remote.py +0 -1298
  79. qolsys_controller-0.0.44.dist-info/METADATA +0 -93
  80. qolsys_controller-0.0.44.dist-info/RECORD +0 -68
  81. {qolsys_controller-0.0.44.dist-info → qolsys_controller-0.0.87.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,16 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from qolsys_controller.adc_service import QolsysAdcService
7
+ from qolsys_controller.adc_service_garagedoor import QolsysAdcGarageDoorService
8
+ from qolsys_controller.zwave_energy_clamp import QolsysEnergyClamp
9
+ from qolsys_controller.zwave_thermometer import QolsysThermometer
2
10
 
11
+ from .adc_device import QolsysAdcDevice
3
12
  from .observable import QolsysObservable
4
- from .partition import QolsysPartition
5
- from .scene import QolsysScene
6
- from .zone import QolsysZone
13
+ from .weather import QolsysWeather
7
14
  from .zwave_device import QolsysZWaveDevice
8
15
  from .zwave_dimmer import QolsysDimmer
9
16
  from .zwave_generic import QolsysGeneric
@@ -12,20 +19,29 @@ from .zwave_thermostat import QolsysThermostat
12
19
 
13
20
  LOGGER = logging.getLogger(__name__)
14
21
 
22
+ if TYPE_CHECKING:
23
+ from .controller import QolsysController
24
+ from .partition import QolsysPartition
25
+ from .scene import QolsysScene
26
+ from .zone import QolsysZone
27
+ from .zwave_device import QolsysZWaveDevice
15
28
 
16
- class QolsysState(QolsysObservable):
17
29
 
18
- def __init__(self) -> None:
30
+ class QolsysState(QolsysObservable):
31
+ def __init__(self, controller: QolsysController) -> None:
19
32
  super().__init__()
20
-
21
- self._partitions = []
22
- self._zones = []
23
- self._zwave_devices = []
24
- self._scenes = []
33
+ self._controller: QolsysController = controller
34
+ self._weather: QolsysWeather = QolsysWeather()
35
+ self._partitions: list[QolsysPartition] = []
36
+ self._zones: list[QolsysZone] = []
37
+ self._adc_devices: list[QolsysAdcDevice] = []
38
+ self._zwave_devices: list[QolsysZWaveDevice] = []
39
+ self._scenes: list[QolsysScene] = []
25
40
 
26
41
  self._state_partition_observer = QolsysObservable()
27
42
  self._state_zone_observer = QolsysObservable()
28
43
  self._state_zwave_observer = QolsysObservable()
44
+ self._state_adc_observer = QolsysObservable()
29
45
  self._state_scene_observer = QolsysObservable()
30
46
 
31
47
  @property
@@ -36,6 +52,10 @@ class QolsysState(QolsysObservable):
36
52
  def zwave_devices(self) -> list[QolsysZWaveDevice]:
37
53
  return self._zwave_devices
38
54
 
55
+ @property
56
+ def adc_devices(self) -> list[QolsysAdcDevice]:
57
+ return self._adc_devices
58
+
39
59
  @property
40
60
  def zones(self) -> list[QolsysZone]:
41
61
  return self._zones
@@ -44,6 +64,10 @@ class QolsysState(QolsysObservable):
44
64
  def scenes(self) -> list[QolsysScene]:
45
65
  return self._scenes
46
66
 
67
+ @property
68
+ def weather(self) -> QolsysWeather:
69
+ return self._weather
70
+
47
71
  @property
48
72
  def zwave_dimmers(self) -> list[QolsysDimmer]:
49
73
  dimmers = []
@@ -68,9 +92,28 @@ class QolsysState(QolsysObservable):
68
92
  for device in self.zwave_devices:
69
93
  if isinstance(device, QolsysThermostat):
70
94
  thermostats.append(device)
71
-
72
95
  return thermostats
73
96
 
97
+ @property
98
+ def zwave_meters(self) -> list[QolsysEnergyClamp]:
99
+ meters = []
100
+ for device in self.zwave_devices:
101
+ if isinstance(device, QolsysEnergyClamp):
102
+ meters.append(device)
103
+ return meters
104
+
105
+ @property
106
+ def zwave_thermometers(self) -> list[QolsysThermometer]:
107
+ thermometer = []
108
+ for device in self.zwave_devices:
109
+ if isinstance(device, QolsysThermometer):
110
+ thermometer.append(device)
111
+ return thermometer
112
+
113
+ @property
114
+ def zwave_other_devices(self) -> list[dict[str, str]]:
115
+ return self._controller.panel.db.get_zwave_other_devices()
116
+
74
117
  @property
75
118
  def state_partition_observer(self) -> QolsysObservable:
76
119
  return self._state_partition_observer
@@ -87,7 +130,11 @@ class QolsysState(QolsysObservable):
87
130
  def state_scene_observer(self) -> QolsysObservable:
88
131
  return self._state_scene_observer
89
132
 
90
- def partition(self, partition_id: str) -> QolsysPartition:
133
+ @property
134
+ def state_adc_observer(self) -> QolsysObservable:
135
+ return self._state_adc_observer
136
+
137
+ def partition(self, partition_id: str) -> QolsysPartition | None:
91
138
  for partition in self.partitions:
92
139
  if partition.id == partition_id:
93
140
  return partition
@@ -97,8 +144,11 @@ class QolsysState(QolsysObservable):
97
144
  def partition_add(self, new_partition: QolsysPartition) -> None:
98
145
  for partition in self.partitions:
99
146
  if new_partition.id == partition.id:
100
- LOGGER.debug("Adding Partition to State, Partition%s (%s) - Allready in Partitions List",
101
- new_partition.id, partition.name)
147
+ LOGGER.debug(
148
+ "Adding Partition to State, Partition%s (%s) - Allready in Partitions List",
149
+ new_partition.id,
150
+ partition.name,
151
+ )
102
152
  return
103
153
 
104
154
  self.partitions.append(new_partition)
@@ -115,7 +165,7 @@ class QolsysState(QolsysObservable):
115
165
  self.partitions.remove(partition)
116
166
  self.state_partition_observer.notify()
117
167
 
118
- def scene(self, scene_id: str) -> QolsysScene:
168
+ def scene(self, scene_id: str) -> QolsysScene | None:
119
169
  for scene in self.scenes:
120
170
  if scene.scene_id == scene_id:
121
171
  return scene
@@ -125,7 +175,7 @@ class QolsysState(QolsysObservable):
125
175
  def scene_add(self, new_scene: QolsysScene) -> None:
126
176
  for scene in self.scenes:
127
177
  if new_scene.scene_id == scene.scene_id:
128
- LOGGER.debug("Adding Scene to State, Scene%s (%s) - Allready in Scene List", new_scene.scene_id, scene.name )
178
+ LOGGER.debug("Adding Scene to State, Scene%s (%s) - Allready in Scene List", new_scene.scene_id, scene.name)
129
179
  return
130
180
 
131
181
  self.scenes.append(new_scene)
@@ -143,7 +193,7 @@ class QolsysState(QolsysObservable):
143
193
  self.scenes.remove(scene)
144
194
  self.state_scene_observer.notify()
145
195
 
146
- def zone(self, zone_id: str) -> QolsysZone:
196
+ def zone(self, zone_id: str) -> QolsysZone | None:
147
197
  for zone in self.zones:
148
198
  if zone.zone_id == zone_id:
149
199
  return zone
@@ -151,14 +201,16 @@ class QolsysState(QolsysObservable):
151
201
 
152
202
  def zone_from_short_id(self, short_id: int) -> QolsysZone | None:
153
203
  for zone in self.zones:
154
- if zone.shortID == short_id:
204
+ if zone.shortID == str(short_id):
155
205
  return zone
156
206
  return None
157
207
 
158
208
  def zone_add(self, new_zone: QolsysZone) -> None:
159
209
  for zone in self.zones:
160
210
  if new_zone.zone_id == zone.zone_id:
161
- LOGGER.debug("Adding Zone to State, zone%s (%s) - Allready in Zone List", new_zone.zone_id, self.sensorname)
211
+ LOGGER.debug(
212
+ "Adding Zone to State, zone%s (%s) - Allready in Zone List", new_zone.zone_id, new_zone.sensorname
213
+ )
162
214
  return
163
215
 
164
216
  self.zones.append(new_zone)
@@ -175,18 +227,24 @@ class QolsysState(QolsysObservable):
175
227
  self.zones.remove(zone)
176
228
  self.state_zone_observer.notify()
177
229
 
178
- def zwave_device(self, node_id: str) -> QolsysZWaveDevice:
230
+ def zwave_device(self, node_id: str) -> QolsysZWaveDevice | None:
179
231
  for zwave_device in self.zwave_devices:
180
232
  if zwave_device.node_id == node_id:
181
233
  return zwave_device
234
+ return None
182
235
 
236
+ def zwave_thermostat(self, node_id: str) -> QolsysThermostat | None:
237
+ thermostat = self.zwave_device(node_id)
238
+ if isinstance(thermostat, QolsysThermostat):
239
+ return thermostat
183
240
  return None
184
241
 
185
242
  def zwave_add(self, new_zwave: QolsysZWaveDevice) -> None:
186
243
  for zwave_device in self.zwave_devices:
187
244
  if new_zwave.node_id == zwave_device.node_id:
188
- LOGGER.debug("Adding ZWave to State, ZWave%s (%s) - Allready in ZWave List",
189
- new_zwave.node_id, zwave_device.node_name)
245
+ LOGGER.debug(
246
+ "Adding ZWave to State, ZWave%s (%s) - Allready in ZWave List", new_zwave.node_id, zwave_device.node_name
247
+ )
190
248
  return
191
249
 
192
250
  self.zwave_devices.append(new_zwave)
@@ -203,8 +261,62 @@ class QolsysState(QolsysObservable):
203
261
  self.zwave_devices.remove(zwave)
204
262
  self.state_zwave_observer.notify()
205
263
 
206
- def sync_zwave_devices_data(self, db_zwaves: list[QolsysZWaveDevice]) -> None: # noqa: PLR0912
264
+ def adc_device(self, device_id: str) -> QolsysAdcDevice | None:
265
+ for adc_device in self.adc_devices:
266
+ if adc_device.device_id == device_id:
267
+ return adc_device
268
+ return None
207
269
 
270
+ def adc_add(self, new_adc: QolsysAdcDevice) -> None:
271
+ for adc_device in self.adc_devices:
272
+ if new_adc.device_id == adc_device.device_id:
273
+ LOGGER.debug("Adding ADC to State, ADC%s (%s) - Allready in ADC List", new_adc.device_id, new_adc.name)
274
+ return
275
+
276
+ self.adc_devices.append(new_adc)
277
+ self.adc_devices.sort(key=lambda x: x.device_id, reverse=False)
278
+ self.state_adc_observer.notify()
279
+
280
+ def adc_delete(self, device_id: str) -> None:
281
+ adc = self.adc_device(device_id)
282
+
283
+ if adc is None:
284
+ LOGGER.debug("Deleting ADC from State, ADC%s not found", device_id)
285
+ return
286
+
287
+ self.adc_devices.remove(adc)
288
+ self.state_adc_observer.notify()
289
+
290
+ def sync_adc_devices_data(self, db_adcs: list[QolsysAdcDevice]) -> None:
291
+ db_adc_list = []
292
+ for db_adc in db_adcs:
293
+ db_adc_list.append(db_adc.device_id)
294
+
295
+ state_adc_list = []
296
+ for state_adc in self.adc_devices:
297
+ state_adc_list.append(state_adc.device_id)
298
+
299
+ # Update existing ADC devices
300
+ for state_adc in self.adc_devices:
301
+ if state_adc.device_id in db_adc_list:
302
+ for db_adc in db_adcs:
303
+ if state_adc.device_id == db_adc.device_id:
304
+ LOGGER.debug("sync_data - update ADC%s", state_adc.device_id)
305
+ state_adc.update_adc_device(db_adc.to_dict_adc())
306
+
307
+ # Add new ADC devices
308
+ for db_adc in db_adcs:
309
+ if db_adc.device_id not in state_adc_list:
310
+ LOGGER.debug("sync_data - add ADC%s", db_adc.device_id)
311
+ self.adc_add(db_adc)
312
+
313
+ # Delete ADC device
314
+ for state_adc in self.adc_devices:
315
+ if state_adc.device_id not in db_adc_list:
316
+ LOGGER.debug("sync_data - delete ADC%s", state_adc.device_id)
317
+ self.adc_delete(state_adc.device_id)
318
+
319
+ def sync_zwave_devices_data(self, db_zwaves: list[QolsysZWaveDevice]) -> None: # noqa: PLR0912
208
320
  db_zwave_list = []
209
321
  for db_zwave in db_zwaves:
210
322
  db_zwave_list.append(db_zwave.node_id)
@@ -229,13 +341,18 @@ class QolsysState(QolsysObservable):
229
341
  # Update Thermostat
230
342
  if isinstance(state_zwave, QolsysThermostat) and isinstance(db_zwave, QolsysThermostat):
231
343
  state_zwave.update_base(db_zwave.to_dict_base())
232
- state_zwave.update_thermostat(db_zwave.to_dict_thermostat)
344
+ state_zwave.update_thermostat(db_zwave.to_dict_thermostat())
233
345
  break
234
346
 
235
347
  # Update Lock
236
348
  if isinstance(state_zwave, QolsysLock) and isinstance(db_zwave, QolsysLock):
237
349
  state_zwave.update_base(db_zwave.to_dict_base())
238
- state_zwave.update_lock(db_zwave.to_dict_lock)
350
+ state_zwave.update_lock(db_zwave.to_dict_lock())
351
+ break
352
+
353
+ # Update Energy Clamp
354
+ if isinstance(state_zwave, QolsysEnergyClamp) and isinstance(db_zwave, QolsysEnergyClamp):
355
+ state_zwave.update_base(db_zwave.to_dict_base())
239
356
  break
240
357
 
241
358
  # Generic Z-Wave Device
@@ -243,10 +360,6 @@ class QolsysState(QolsysObservable):
243
360
  state_zwave.update_base(db_zwave.to_dict_base())
244
361
  break
245
362
 
246
- # zwave node_id has changed of node_type, delete and add again
247
- # self.zwave_delete(int(state_zwave.node_id))
248
- # self.zwave_add(db_zwave)
249
-
250
363
  # Add new zwave device
251
364
  for db_zwave in db_zwaves:
252
365
  if db_zwave.node_id not in state_zwave_list:
@@ -256,9 +369,13 @@ class QolsysState(QolsysObservable):
256
369
  # Delete zwave device
257
370
  for state_zwave in self.zwave_devices:
258
371
  if state_zwave.node_id not in db_zwave_list:
259
- LOGGER.debug("sync_data - delete ZWave%s", state_zwave.none_id)
372
+ LOGGER.debug("sync_data - delete ZWave%s", state_zwave.node_id)
260
373
  self.zwave_delete(state_zwave.node_id)
261
374
 
375
+ def sync_weather_data(self, db_weather: QolsysWeather) -> None:
376
+ LOGGER.debug("sync_data - update Weather")
377
+ self._weather.update(db_weather.forecasts)
378
+
262
379
  def sync_scenes_data(self, db_scenes: list[QolsysScene]) -> None:
263
380
  db_scene_list = []
264
381
  for db_scene in db_scenes:
@@ -351,8 +468,31 @@ class QolsysState(QolsysObservable):
351
468
  LOGGER.debug("sync_data - Add Partition%s", db_partition.id)
352
469
  self.partition_add(db_partition)
353
470
 
354
- def dump(self) -> None: # noqa: PLR0915
355
- LOGGER.debug("*** Information ***")
471
+ def dump(self) -> None: # noqa: PLR0912, PLR0915
472
+ LOGGER.debug("*** Device Information ***")
473
+
474
+ def dump_meter(self: QolsysState, device: QolsysZWaveDevice) -> None:
475
+ for endpoint in device.meter_endpoints:
476
+ for meter_sensor in endpoint.sensors:
477
+ LOGGER.debug(
478
+ " Meter%s Endpoint%s - %s - value: %.2f (%s)",
479
+ device.node_id,
480
+ endpoint.endpoint,
481
+ endpoint._meter_type.name,
482
+ meter_sensor.value,
483
+ meter_sensor.scale.name,
484
+ )
485
+
486
+ def dump_multilevelsensor(self: QolsysState, device: QolsysZWaveDevice) -> None:
487
+ for endpoint in device.multilevelsensor_endpoints:
488
+ for sensor in endpoint.sensors:
489
+ LOGGER.debug(
490
+ " Multilevelsensor%s Endpoint%s - value: %.2f (%s)",
491
+ device.node_id,
492
+ endpoint.endpoint,
493
+ sensor.value,
494
+ sensor.unit.name,
495
+ )
356
496
 
357
497
  for partition in self.partitions:
358
498
  pid = partition.id
@@ -394,6 +534,8 @@ class QolsysState(QolsysObservable):
394
534
  LOGGER.debug("Dimmer%s (%s) - node_status: %s", nid, name, zwave.node_status)
395
535
  LOGGER.debug("Dimmer%s (%s) - battery_level: %s", nid, name, zwave.node_battery_level)
396
536
  LOGGER.debug("Dimmer%s (%s) - battery_level_value: %s", nid, name, zwave.node_battery_level_value)
537
+ dump_meter(self, zwave)
538
+ dump_multilevelsensor(self, zwave)
397
539
  continue
398
540
 
399
541
  if isinstance(zwave, QolsysThermostat):
@@ -406,12 +548,32 @@ class QolsysState(QolsysObservable):
406
548
  LOGGER.debug("Thermostat%s (%s) - target_cool_temp: %s", zid, name, zwave.thermostat_target_cool_temp)
407
549
  LOGGER.debug("Thermostat%s (%s) - target_heat_temp: %s", zid, name, zwave.thermostat_target_heat_temp)
408
550
  LOGGER.debug("Thermostat%s (%s) - set_point_mode: %s", zid, name, zwave.thermostat_set_point_mode)
551
+ dump_meter(self, zwave)
552
+ dump_multilevelsensor(self, zwave)
409
553
  continue
410
554
 
411
555
  if isinstance(zwave, QolsysLock):
412
556
  zid = zwave.lock_node_id
413
557
  name = zwave.lock_name
414
- LOGGER.debug("Lock%s (%s) - current_temp: %s", zid, name, zwave.lock_status)
558
+ LOGGER.debug("Lock%s (%s) - lock_status: %s", zid, name, zwave.lock_status)
559
+ dump_meter(self, zwave)
560
+ dump_multilevelsensor(self, zwave)
561
+ continue
562
+
563
+ if isinstance(zwave, QolsysEnergyClamp):
564
+ nid = zwave.node_id
565
+ name = zwave.node_name
566
+ LOGGER.debug("EnergyClamp%s (%s)", nid, name)
567
+ dump_meter(self, zwave)
568
+ dump_multilevelsensor(self, zwave)
569
+ continue
570
+
571
+ if isinstance(zwave, QolsysThermometer):
572
+ nid = zwave.node_id
573
+ name = zwave.node_name
574
+ LOGGER.debug("Thermometer%s (%s)", nid, name)
575
+ dump_meter(self, zwave)
576
+ dump_multilevelsensor(self, zwave)
415
577
  continue
416
578
 
417
579
  if isinstance(zwave, QolsysGeneric):
@@ -421,9 +583,34 @@ class QolsysState(QolsysObservable):
421
583
  LOGGER.debug("Generic%s (%s) - status: %s", zid, name, zwave.node_status)
422
584
  LOGGER.debug("Generic%s (%s) - battery_level: %s", zid, name, zwave.node_battery_level)
423
585
  LOGGER.debug("Generic%s (%s) - battery_level_vale: %s", zid, name, zwave.node_battery_level_value)
586
+ dump_meter(self, zwave)
587
+ dump_multilevelsensor(self, zwave)
424
588
  continue
425
589
 
590
+ LOGGER.debug("Other Z-Wave devices information")
591
+ LOGGER.debug(self.zwave_other_devices)
592
+
593
+ for adc in self.adc_devices:
594
+ for service in adc.services:
595
+ if isinstance(service, QolsysAdcGarageDoorService):
596
+ LOGGER.debug(
597
+ "ADC%s GarageDoor%s (%s) - state: %s", adc.device_id, service.id, adc.name, service.func_state
598
+ )
599
+ continue
600
+
601
+ if isinstance(service, QolsysAdcService):
602
+ LOGGER.debug("ADC%s Service%s (%s) - state: %s", adc.device_id, service.id, adc.name, service.func_state)
603
+
426
604
  for scene in self.scenes:
427
605
  sid = scene.scene_id
428
606
  name = scene.name
429
- LOGGER.debug("Scene%s (%s)",sid, name)
607
+ LOGGER.debug("Scene%s (%s)", sid, name)
608
+
609
+ for forecast in self.weather.forecasts:
610
+ LOGGER.debug(
611
+ "Weather - %s - High: %s, Low:%s, Condition: %s",
612
+ forecast.day_of_week[0:3],
613
+ forecast.high_temp,
614
+ forecast.low_temp,
615
+ forecast.condition,
616
+ )
@@ -7,32 +7,32 @@ LOGGER = logging.getLogger(__name__)
7
7
 
8
8
  class QolsysTaskManager:
9
9
  def __init__(self) -> None:
10
- self._tasks = set()
10
+ self._tasks: set[asyncio.Task] = set() # type: ignore[type-arg]
11
11
 
12
- def run(self, coro: Coroutine, label: str) -> asyncio.Task:
12
+ def run(self, coro: Coroutine, label: str) -> asyncio.Task: # type: ignore[type-arg]
13
13
  task = asyncio.create_task(coro, name=label)
14
14
  self._tasks.add(task)
15
15
 
16
- def _done_callback(task: asyncio.Task) -> None:
17
-
16
+ def _done_callback(task: asyncio.Task) -> None: # type: ignore[type-arg]
18
17
  try:
19
18
  task.result()
20
19
 
21
20
  except asyncio.CancelledError:
22
- LOGGER.debug("Task Cancelled: %s",task.get_name())
21
+ LOGGER.debug("Task Cancelled: %s", task.get_name())
23
22
 
24
- except Exception as e: # noqa: BLE001
25
- LOGGER.debug("[Callback] Task failed with: %s",e)
23
+ except Exception: # noqa: BLE001
24
+ LOGGER.exception("[Callback] Task failed: %s", task.get_name())
26
25
 
27
- self._tasks.discard(task)
26
+ finally:
27
+ self._tasks.discard(task)
28
28
 
29
29
  task.add_done_callback(_done_callback)
30
30
  return task
31
31
 
32
- def get_task(self, label:str) -> asyncio.Task | None:
32
+ def get_task(self, label: str) -> asyncio.Task | None: # type: ignore[type-arg]
33
33
  for task in self._tasks:
34
- if task.get_name() == label:
35
- return task
34
+ if task.get_name() == label:
35
+ return task
36
36
  return None
37
37
 
38
38
  def cancel(self, label: str) -> None:
@@ -49,9 +49,6 @@ class QolsysTaskManager:
49
49
  if self._tasks:
50
50
  await asyncio.gather(*self._tasks, return_exceptions=True)
51
51
 
52
- def pending(self) -> None:
53
- return {t for t in self._tasks if not t.done()}
54
-
55
52
  def dump(self) -> None:
56
53
  for task in self._tasks:
57
54
  LOGGER.debug("Task: %s, Done: %s, Cancelled: %s", task.get_name(), task.done(), task.cancelled())
@@ -0,0 +1,25 @@
1
+ import logging
2
+
3
+ LOGGER = logging.getLogger(__name__)
4
+
5
+
6
+ class QolsysUser:
7
+ def __init__(self) -> None:
8
+ self._id: int = 0
9
+ self._user_code = ""
10
+
11
+ @property
12
+ def id(self) -> int:
13
+ return self._id
14
+
15
+ @id.setter
16
+ def id(self, value: int) -> None:
17
+ self._id = value
18
+
19
+ @property
20
+ def user_code(self) -> str:
21
+ return self._user_code
22
+
23
+ @user_code.setter
24
+ def user_code(self, value: str) -> None:
25
+ self._user_code = value
@@ -1,24 +1,16 @@
1
1
  import logging
2
2
  import random
3
- import re
4
3
 
5
4
  LOGGER = logging.getLogger(__name__)
6
5
 
7
6
 
8
- def fix_json_string(json_string):
9
- def replace_escape(match):
10
- hex_value = match.group(1)
11
- decimal_value = int(hex_value, 16)
12
- return f"\\u{decimal_value:04x}"
13
-
14
- return re.sub(r"\\x([0-9a-fA-F]{2})", replace_escape, json_string)
15
-
16
-
17
- def generate_random_mac() -> str:
7
+ def generate_random_mac() -> str: # noqa: D103
18
8
  mac = [
19
- 0xf2, 0x16, 0x3e,
20
- random.randint(0x00, 0x7f),
21
- random.randint(0x00, 0xff),
22
- random.randint(0x00, 0xff),
9
+ 0xF2,
10
+ 0x16,
11
+ 0x3E,
12
+ random.randint(0x00, 0x7F),
13
+ random.randint(0x00, 0xFF),
14
+ random.randint(0x00, 0xFF),
23
15
  ]
24
- return ":".join(map(lambda x: "%02x" % x, mac))
16
+ return ":".join(map(lambda x: "%02x" % x, mac)) # noqa: C417, UP031
@@ -0,0 +1,71 @@
1
+ import logging
2
+
3
+ from .observable import QolsysObservable
4
+
5
+ LOGGER = logging.getLogger(__name__)
6
+
7
+
8
+ class QolsysForecast:
9
+ def __init__(self, data: dict[str, str]) -> None:
10
+ self._high_temp: str = data.get("high_temp", "")
11
+ self._low_temp: str = data.get("low_temp", "")
12
+ self._day_of_week: str = data.get("day_of_week", "")
13
+ self._condition: str = data.get("condition", "")
14
+ self._icon: str = data.get("icon", "")
15
+ self._precipitation: str = data.get("precipitation", "")
16
+ self._current_weather_date: str = data.get("current_weather_date", "")
17
+
18
+ @property
19
+ def high_temp(self) -> float | None:
20
+ try:
21
+ return float(self._high_temp)
22
+ except ValueError:
23
+ return None
24
+
25
+ @property
26
+ def low_temp(self) -> float | None:
27
+ try:
28
+ return float(self._low_temp)
29
+ except ValueError:
30
+ return None
31
+
32
+ @property
33
+ def day_of_week(self) -> str:
34
+ return self._day_of_week
35
+
36
+ @property
37
+ def condition(self) -> str:
38
+ return self._condition
39
+
40
+ @property
41
+ def precipitation(self) -> int | None:
42
+ try:
43
+ return int(self._precipitation)
44
+ except ValueError:
45
+ return None
46
+
47
+ @property
48
+ def current_weather_date(self) -> str:
49
+ return self._current_weather_date
50
+
51
+
52
+ class QolsysWeather(QolsysObservable):
53
+ def __init__(self) -> None:
54
+ super().__init__()
55
+ self._forecasts: list[QolsysForecast] = []
56
+
57
+ def current_weather(self) -> QolsysForecast | None:
58
+ if self._forecasts:
59
+ return self._forecasts[0]
60
+ return None
61
+
62
+ def update(self, data: list[QolsysForecast]) -> None:
63
+ self._forecasts.clear()
64
+ for forecast_data in data:
65
+ self._forecasts.append(forecast_data)
66
+
67
+ self.notify()
68
+
69
+ @property
70
+ def forecasts(self) -> list[QolsysForecast]:
71
+ return self._forecasts