qolsys-controller 0.0.28__py3-none-any.whl → 0.0.51__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.
- qolsys_controller/controller.py +18 -7
- qolsys_controller/database/db.py +31 -0
- qolsys_controller/database/table.py +36 -2
- qolsys_controller/database/table_doorlock.py +1 -0
- qolsys_controller/database/table_eu_event.py +11 -2
- qolsys_controller/database/table_powerg_device.py +25 -1
- qolsys_controller/database/table_sensor.py +2 -0
- qolsys_controller/database/table_user.py +1 -0
- qolsys_controller/database/table_zwave_node.py +1 -0
- qolsys_controller/database/table_zwave_other.py +14 -1
- qolsys_controller/enum.py +9 -5
- qolsys_controller/enum_zwave.py +0 -1
- qolsys_controller/mdns.py +8 -3
- qolsys_controller/panel.py +134 -45
- qolsys_controller/partition.py +14 -1
- qolsys_controller/plugin.py +10 -20
- qolsys_controller/plugin_c4.py +1 -1
- qolsys_controller/plugin_remote.py +165 -181
- qolsys_controller/settings.py +53 -19
- qolsys_controller/state.py +53 -13
- qolsys_controller/task_manager.py +18 -1
- qolsys_controller/weather.py +74 -0
- qolsys_controller/zone.py +148 -10
- qolsys_controller/zwave_device.py +1 -1
- qolsys_controller/zwave_lock.py +1 -1
- {qolsys_controller-0.0.28.dist-info → qolsys_controller-0.0.51.dist-info}/METADATA +5 -4
- {qolsys_controller-0.0.28.dist-info → qolsys_controller-0.0.51.dist-info}/RECORD +29 -28
- {qolsys_controller-0.0.28.dist-info → qolsys_controller-0.0.51.dist-info}/WHEEL +0 -0
- {qolsys_controller-0.0.28.dist-info → qolsys_controller-0.0.51.dist-info}/licenses/LICENSE +0 -0
qolsys_controller/settings.py
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
3
6
|
|
|
4
7
|
LOGGER = logging.getLogger(__name__)
|
|
5
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .controller import QolsysController
|
|
11
|
+
|
|
6
12
|
|
|
7
13
|
class QolsysSettings:
|
|
8
14
|
|
|
9
|
-
def __init__(self) -> None:
|
|
15
|
+
def __init__(self, controller: QolsysController) -> None:
|
|
16
|
+
|
|
17
|
+
# Main controller
|
|
18
|
+
self._controller = controller
|
|
10
19
|
|
|
11
20
|
# Plugin
|
|
12
|
-
self._plugin_ip = ""
|
|
13
|
-
self._random_mac = ""
|
|
14
|
-
self._panel_mac = ""
|
|
15
|
-
self._panel_ip = ""
|
|
21
|
+
self._plugin_ip: str = ""
|
|
22
|
+
self._random_mac: str = ""
|
|
23
|
+
self._panel_mac: str = ""
|
|
24
|
+
self._panel_ip: str = ""
|
|
16
25
|
|
|
26
|
+
# Path
|
|
17
27
|
self._config_directory: Path = Path()
|
|
18
28
|
self._pki_directory: Path = Path()
|
|
19
29
|
self._media_directory: Path = Path()
|
|
@@ -25,8 +35,12 @@ class QolsysSettings:
|
|
|
25
35
|
# MQTT
|
|
26
36
|
self._mqtt_timeout: int = 30
|
|
27
37
|
self._mqtt_ping: int = 600
|
|
28
|
-
self._mqtt_qos:int = 0
|
|
29
|
-
self._mqtt_remote_client_id = ""
|
|
38
|
+
self._mqtt_qos: int = 0
|
|
39
|
+
self._mqtt_remote_client_id: str = ""
|
|
40
|
+
|
|
41
|
+
# Operation
|
|
42
|
+
self._motion_sensor_delay: bool = True
|
|
43
|
+
self._motion_sensor_delay_sec: int = 310
|
|
30
44
|
|
|
31
45
|
@property
|
|
32
46
|
def random_mac(self) -> str:
|
|
@@ -52,6 +66,30 @@ class QolsysSettings:
|
|
|
52
66
|
def panel_ip(self) -> str:
|
|
53
67
|
return self._panel_ip
|
|
54
68
|
|
|
69
|
+
@property
|
|
70
|
+
def mqtt_timeout(self) -> int:
|
|
71
|
+
return self._mqtt_timeout
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def mqtt_ping(self) -> int:
|
|
75
|
+
return self._mqtt_ping
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def motion_sensor_delay(self) -> bool:
|
|
79
|
+
return self._motion_sensor_delay
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def motion_sensor_delay_sec(self) -> int:
|
|
83
|
+
return self._motion_sensor_delay_sec
|
|
84
|
+
|
|
85
|
+
@motion_sensor_delay.setter
|
|
86
|
+
def motion_sensor_delay(self, value: bool) -> None:
|
|
87
|
+
self._motion_sensor_delay = value
|
|
88
|
+
|
|
89
|
+
@motion_sensor_delay_sec.setter
|
|
90
|
+
def motion_sensor_delay_sec(self, value: int) -> None:
|
|
91
|
+
self._motion_sensor_delay_sec = value
|
|
92
|
+
|
|
55
93
|
@panel_ip.setter
|
|
56
94
|
def panel_ip(self, panel_ip: str) -> None:
|
|
57
95
|
self._panel_ip = panel_ip
|
|
@@ -61,7 +99,7 @@ class QolsysSettings:
|
|
|
61
99
|
self._plugin_ip = plugin_ip
|
|
62
100
|
|
|
63
101
|
@property
|
|
64
|
-
def config_directory(self) ->
|
|
102
|
+
def config_directory(self) -> Path:
|
|
65
103
|
return self._config_directory
|
|
66
104
|
|
|
67
105
|
@config_directory.setter
|
|
@@ -83,17 +121,13 @@ class QolsysSettings:
|
|
|
83
121
|
def key_size(self) -> int:
|
|
84
122
|
return self._key_size
|
|
85
123
|
|
|
86
|
-
@
|
|
87
|
-
def mqtt_timeout(self) ->
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def mqtt_ping(self) -> int:
|
|
92
|
-
return self._mqtt_ping
|
|
124
|
+
@mqtt_timeout.setter
|
|
125
|
+
def mqtt_timeout(self, value: int) -> None:
|
|
126
|
+
self._mqtt_timeout = value
|
|
93
127
|
|
|
94
128
|
@mqtt_ping.setter
|
|
95
|
-
def mqtt_ping(self,
|
|
96
|
-
self._mqtt_ping =
|
|
129
|
+
def mqtt_ping(self, value: int) -> None:
|
|
130
|
+
self._mqtt_ping = value
|
|
97
131
|
|
|
98
132
|
@property
|
|
99
133
|
def mqtt_qos(self) -> int:
|
|
@@ -104,7 +138,7 @@ class QolsysSettings:
|
|
|
104
138
|
return self._mqtt_remote_client_id
|
|
105
139
|
|
|
106
140
|
@mqtt_remote_client_id.setter
|
|
107
|
-
def mqtt_remote_client_id(self,client_id:str) -> None:
|
|
141
|
+
def mqtt_remote_client_id(self,client_id: str) -> None:
|
|
108
142
|
self._mqtt_remote_client_id = client_id
|
|
109
143
|
|
|
110
144
|
def check_panel_ip(self) -> bool:
|
|
@@ -123,7 +157,7 @@ class QolsysSettings:
|
|
|
123
157
|
LOGGER.debug("Found Plugin IP: %s", self._plugin_ip)
|
|
124
158
|
return True
|
|
125
159
|
|
|
126
|
-
def check_config_directory(self, create: bool = True) -> bool:
|
|
160
|
+
def check_config_directory(self, create: bool = True) -> bool: # noqa: PLR0911
|
|
127
161
|
if not self.config_directory.is_dir():
|
|
128
162
|
if not create:
|
|
129
163
|
LOGGER.debug("config_directory not found: %s", self.config_directory)
|
qolsys_controller/state.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
2
5
|
|
|
3
6
|
from .observable import QolsysObservable
|
|
4
|
-
from .
|
|
5
|
-
from .scene import QolsysScene
|
|
6
|
-
from .zone import QolsysZone
|
|
7
|
+
from .weather import QolsysWeather
|
|
7
8
|
from .zwave_device import QolsysZWaveDevice
|
|
8
9
|
from .zwave_dimmer import QolsysDimmer
|
|
9
10
|
from .zwave_generic import QolsysGeneric
|
|
@@ -12,12 +13,23 @@ from .zwave_thermostat import QolsysThermostat
|
|
|
12
13
|
|
|
13
14
|
LOGGER = logging.getLogger(__name__)
|
|
14
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from .controller import QolsysController
|
|
18
|
+
from .partition import QolsysPartition
|
|
19
|
+
from .scene import QolsysScene
|
|
20
|
+
from .zone import QolsysZone
|
|
21
|
+
from .zwave_device import QolsysZWaveDevice
|
|
22
|
+
|
|
15
23
|
|
|
16
24
|
class QolsysState(QolsysObservable):
|
|
17
25
|
|
|
18
|
-
def __init__(self) -> None:
|
|
26
|
+
def __init__(self, controller: QolsysController) -> None:
|
|
19
27
|
super().__init__()
|
|
20
28
|
|
|
29
|
+
self._controller = controller
|
|
30
|
+
|
|
31
|
+
self._weather = QolsysWeather()
|
|
32
|
+
|
|
21
33
|
self._partitions = []
|
|
22
34
|
self._zones = []
|
|
23
35
|
self._zwave_devices = []
|
|
@@ -32,6 +44,10 @@ class QolsysState(QolsysObservable):
|
|
|
32
44
|
def partitions(self) -> list[QolsysPartition]:
|
|
33
45
|
return self._partitions
|
|
34
46
|
|
|
47
|
+
@property
|
|
48
|
+
def weather(self) -> QolsysWeather:
|
|
49
|
+
return self._weather
|
|
50
|
+
|
|
35
51
|
@property
|
|
36
52
|
def zwave_devices(self) -> list[QolsysZWaveDevice]:
|
|
37
53
|
return self._zwave_devices
|
|
@@ -102,6 +118,7 @@ class QolsysState(QolsysObservable):
|
|
|
102
118
|
return
|
|
103
119
|
|
|
104
120
|
self.partitions.append(new_partition)
|
|
121
|
+
self.partitions.sort(key=lambda x: x.id, reverse=False)
|
|
105
122
|
self.state_partition_observer.notify()
|
|
106
123
|
|
|
107
124
|
def partition_delete(self, partition_id: str) -> None:
|
|
@@ -128,6 +145,8 @@ class QolsysState(QolsysObservable):
|
|
|
128
145
|
return
|
|
129
146
|
|
|
130
147
|
self.scenes.append(new_scene)
|
|
148
|
+
self.scenes.sort(key=lambda x: x.scene_id, reverse=False)
|
|
149
|
+
|
|
131
150
|
self.state_scene_observer.notify()
|
|
132
151
|
|
|
133
152
|
def scene_delete(self, scene_id: str) -> None:
|
|
@@ -144,7 +163,12 @@ class QolsysState(QolsysObservable):
|
|
|
144
163
|
for zone in self.zones:
|
|
145
164
|
if zone.zone_id == zone_id:
|
|
146
165
|
return zone
|
|
166
|
+
return None
|
|
147
167
|
|
|
168
|
+
def zone_from_short_id(self, short_id: int) -> QolsysZone | None:
|
|
169
|
+
for zone in self.zones:
|
|
170
|
+
if zone.shortID == short_id:
|
|
171
|
+
return zone
|
|
148
172
|
return None
|
|
149
173
|
|
|
150
174
|
def zone_add(self, new_zone: QolsysZone) -> None:
|
|
@@ -154,6 +178,7 @@ class QolsysState(QolsysObservable):
|
|
|
154
178
|
return
|
|
155
179
|
|
|
156
180
|
self.zones.append(new_zone)
|
|
181
|
+
self.zones.sort(key=lambda x: x.zone_id, reverse=False)
|
|
157
182
|
self.state_zone_observer.notify()
|
|
158
183
|
|
|
159
184
|
def zone_delete(self, zone_id: str) -> None:
|
|
@@ -181,6 +206,7 @@ class QolsysState(QolsysObservable):
|
|
|
181
206
|
return
|
|
182
207
|
|
|
183
208
|
self.zwave_devices.append(new_zwave)
|
|
209
|
+
self.zwave_devices.sort(key=lambda x: x.node_id, reverse=False)
|
|
184
210
|
self.state_zwave_observer.notify()
|
|
185
211
|
|
|
186
212
|
def zwave_delete(self, node_id: str) -> None:
|
|
@@ -193,7 +219,7 @@ class QolsysState(QolsysObservable):
|
|
|
193
219
|
self.zwave_devices.remove(zwave)
|
|
194
220
|
self.state_zwave_observer.notify()
|
|
195
221
|
|
|
196
|
-
def sync_zwave_devices_data(self, db_zwaves: list[QolsysZWaveDevice]) -> None: # noqa:
|
|
222
|
+
def sync_zwave_devices_data(self, db_zwaves: list[QolsysZWaveDevice]) -> None: # noqa: PLR0912
|
|
197
223
|
|
|
198
224
|
db_zwave_list = []
|
|
199
225
|
for db_zwave in db_zwaves:
|
|
@@ -247,7 +273,11 @@ class QolsysState(QolsysObservable):
|
|
|
247
273
|
for state_zwave in self.zwave_devices:
|
|
248
274
|
if state_zwave.node_id not in db_zwave_list:
|
|
249
275
|
LOGGER.debug("sync_data - delete ZWave%s", state_zwave.none_id)
|
|
250
|
-
self.zwave_delete(
|
|
276
|
+
self.zwave_delete(state_zwave.node_id)
|
|
277
|
+
|
|
278
|
+
def sync_weather_data(self, db_weather: QolsysWeather) -> None:
|
|
279
|
+
LOGGER.debug("sync_data - update Weather")
|
|
280
|
+
self._weather.update(db_weather.forecasts)
|
|
251
281
|
|
|
252
282
|
def sync_scenes_data(self, db_scenes: list[QolsysScene]) -> None:
|
|
253
283
|
db_scene_list = []
|
|
@@ -271,7 +301,7 @@ class QolsysState(QolsysObservable):
|
|
|
271
301
|
for state_scene in self.scenes:
|
|
272
302
|
if state_scene.scene_id not in db_scene_list:
|
|
273
303
|
LOGGER.debug("sync_data - delete Scene%s", state_scene.scene_id)
|
|
274
|
-
self.scene_delete(
|
|
304
|
+
self.scene_delete(state_scene.scene_id)
|
|
275
305
|
|
|
276
306
|
# Add new scene
|
|
277
307
|
for db_scene in db_scenes:
|
|
@@ -279,7 +309,7 @@ class QolsysState(QolsysObservable):
|
|
|
279
309
|
LOGGER.debug("sync_data - add Scene%s", db_scene.scene_id)
|
|
280
310
|
self.scene_add(db_scene)
|
|
281
311
|
|
|
282
|
-
def sync_zones_data(self, db_zones: list[QolsysZone]) -> None:
|
|
312
|
+
def sync_zones_data(self, db_zones: list[QolsysZone]) -> None:
|
|
283
313
|
db_zone_list = []
|
|
284
314
|
for db_zone in db_zones:
|
|
285
315
|
db_zone_list.append(db_zone.zone_id)
|
|
@@ -295,12 +325,13 @@ class QolsysState(QolsysObservable):
|
|
|
295
325
|
if state_zone.zone_id == db_zone.zone_id:
|
|
296
326
|
LOGGER.debug("sync_data - update Zone%s", state_zone.zone_id)
|
|
297
327
|
state_zone.update(db_zone.to_dict())
|
|
328
|
+
state_zone.update_powerg(db_zone.to_powerg_dict())
|
|
298
329
|
|
|
299
330
|
# Delete zones
|
|
300
331
|
for state_zone in self.zones:
|
|
301
332
|
if state_zone.zone_id not in db_zone_list:
|
|
302
333
|
LOGGER.debug("sync_data - delete Zone%s", state_zone.zone_id)
|
|
303
|
-
self.zone_delete(
|
|
334
|
+
self.zone_delete(state_zone.zone_id)
|
|
304
335
|
|
|
305
336
|
# Add new zone
|
|
306
337
|
for db_zone in db_zones:
|
|
@@ -308,7 +339,7 @@ class QolsysState(QolsysObservable):
|
|
|
308
339
|
LOGGER.debug("sync_data - add Zone%s", db_zone.zone_id)
|
|
309
340
|
self.zone_add(db_zone)
|
|
310
341
|
|
|
311
|
-
def sync_partitions_data(self, db_partitions: list[QolsysPartition]) -> None:
|
|
342
|
+
def sync_partitions_data(self, db_partitions: list[QolsysPartition]) -> None:
|
|
312
343
|
db_partition_list = []
|
|
313
344
|
for db_partition in db_partitions:
|
|
314
345
|
db_partition_list.append(db_partition.id)
|
|
@@ -340,8 +371,8 @@ class QolsysState(QolsysObservable):
|
|
|
340
371
|
LOGGER.debug("sync_data - Add Partition%s", db_partition.id)
|
|
341
372
|
self.partition_add(db_partition)
|
|
342
373
|
|
|
343
|
-
def dump(self) -> None: # noqa: PLR0915
|
|
344
|
-
LOGGER.debug("*** Information ***")
|
|
374
|
+
def dump(self) -> None: # noqa: PLR0912, PLR0915
|
|
375
|
+
LOGGER.debug("*** Device Information ***")
|
|
345
376
|
|
|
346
377
|
for partition in self.partitions:
|
|
347
378
|
pid = partition.id
|
|
@@ -367,6 +398,12 @@ class QolsysState(QolsysObservable):
|
|
|
367
398
|
LOGGER.debug("Zone%s (%s) - latestdBm: %s", zid, name, zone.latestdBm)
|
|
368
399
|
LOGGER.debug("Zone%s (%s) - averagedBm: %s", zid, name, zone.averagedBm)
|
|
369
400
|
|
|
401
|
+
if zone.is_powerg_temperature_enabled():
|
|
402
|
+
LOGGER.debug("Zone%s (%s) - powerg_temperature: %s", zid, name, zone.powerg_temperature)
|
|
403
|
+
|
|
404
|
+
if zone.is_powerg_light_enabled():
|
|
405
|
+
LOGGER.debug("Zone%s (%s) - powerg_light: %s", zid, name, zone.powerg_light)
|
|
406
|
+
|
|
370
407
|
for zwave in self.zwave_devices:
|
|
371
408
|
if isinstance(zwave, QolsysDimmer):
|
|
372
409
|
nid = zwave.node_id
|
|
@@ -394,7 +431,7 @@ class QolsysState(QolsysObservable):
|
|
|
394
431
|
if isinstance(zwave, QolsysLock):
|
|
395
432
|
zid = zwave.lock_node_id
|
|
396
433
|
name = zwave.lock_name
|
|
397
|
-
LOGGER.debug("Lock%s (%s) -
|
|
434
|
+
LOGGER.debug("Lock%s (%s) - lock_status: %s", zid, name, zwave.lock_status)
|
|
398
435
|
continue
|
|
399
436
|
|
|
400
437
|
if isinstance(zwave, QolsysGeneric):
|
|
@@ -410,3 +447,6 @@ class QolsysState(QolsysObservable):
|
|
|
410
447
|
sid = scene.scene_id
|
|
411
448
|
name = scene.name
|
|
412
449
|
LOGGER.debug("Scene%s (%s)",sid, name)
|
|
450
|
+
|
|
451
|
+
for forecast in self.weather.forecasts:
|
|
452
|
+
LOGGER.debug("Weather - %s - High: %s, Low:%s, Condition: %s", forecast.day_of_week[0:3],forecast.high_temp,forecast.low_temp,forecast.condition)
|
|
@@ -14,19 +14,36 @@ class QolsysTaskManager:
|
|
|
14
14
|
self._tasks.add(task)
|
|
15
15
|
|
|
16
16
|
def _done_callback(task: asyncio.Task) -> None:
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
task.result()
|
|
20
|
+
|
|
21
|
+
except asyncio.CancelledError:
|
|
22
|
+
LOGGER.debug("Task Cancelled: %s",task.get_name())
|
|
23
|
+
|
|
24
|
+
except Exception as e: # noqa: BLE001
|
|
25
|
+
LOGGER.debug("[Callback] Task failed with: %s",e)
|
|
26
|
+
|
|
17
27
|
self._tasks.discard(task)
|
|
18
28
|
|
|
19
29
|
task.add_done_callback(_done_callback)
|
|
20
30
|
return task
|
|
21
31
|
|
|
32
|
+
def get_task(self, label:str) -> asyncio.Task | None:
|
|
33
|
+
for task in self._tasks:
|
|
34
|
+
if task.get_name() == label:
|
|
35
|
+
return task
|
|
36
|
+
return None
|
|
37
|
+
|
|
22
38
|
def cancel(self, label: str) -> None:
|
|
23
39
|
for task in self._tasks:
|
|
24
40
|
if task.get_name() == label:
|
|
25
41
|
task.cancel()
|
|
26
42
|
|
|
27
|
-
def cancel_all(self) -> None:
|
|
43
|
+
async def cancel_all(self) -> None:
|
|
28
44
|
for task in self._tasks:
|
|
29
45
|
task.cancel()
|
|
46
|
+
await task
|
|
30
47
|
|
|
31
48
|
async def wait_all(self) -> None:
|
|
32
49
|
if self._tasks:
|
|
@@ -0,0 +1,74 @@
|
|
|
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) -> None:
|
|
10
|
+
self._high_temp = data.get("high_temp", "")
|
|
11
|
+
self._low_temp = data.get("low_temp", "")
|
|
12
|
+
self._day_of_week = data.get("day_of_week", "")
|
|
13
|
+
self._condition = data.get("condition", "")
|
|
14
|
+
self._icon = data.get("icon", "")
|
|
15
|
+
self._precipitation = data.get("precipitation", "")
|
|
16
|
+
self._current_weather_date = 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
|
|
72
|
+
|
|
73
|
+
def to_dict(self) -> dict:
|
|
74
|
+
pass
|
qolsys_controller/zone.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
|
|
4
|
+
from qolsys_controller.settings import QolsysSettings
|
|
5
|
+
|
|
3
6
|
from .enum import ZoneSensorGroup, ZoneSensorType, ZoneStatus
|
|
4
7
|
from .observable import QolsysObservable
|
|
5
8
|
|
|
@@ -8,9 +11,13 @@ LOGGER = logging.getLogger(__name__)
|
|
|
8
11
|
|
|
9
12
|
class QolsysZone(QolsysObservable):
|
|
10
13
|
|
|
11
|
-
def __init__(self, data: dict) -> None:
|
|
14
|
+
def __init__(self, data: dict, settings: QolsysSettings) -> None: # noqa: PLR0915
|
|
12
15
|
super().__init__()
|
|
13
16
|
|
|
17
|
+
self._settings = settings
|
|
18
|
+
|
|
19
|
+
self._delay_task:asyncio.Task = None
|
|
20
|
+
|
|
14
21
|
self._zone_id = data.get("zoneid", "")
|
|
15
22
|
self._sensorname = data.get("sensorname", "")
|
|
16
23
|
self._sensorstatus: ZoneStatus = ZoneStatus(data.get("sensorstatus", ""))
|
|
@@ -20,6 +27,9 @@ class QolsysZone(QolsysObservable):
|
|
|
20
27
|
self._averagedBm = data.get("averagedBm", "")
|
|
21
28
|
self._latestdBm = data.get("latestdBm", "")
|
|
22
29
|
self._ac_status = data.get("ac_status", "")
|
|
30
|
+
self._shortID = data.get("shortID", "")
|
|
31
|
+
self._device_capability = data.get("device_capability", "")
|
|
32
|
+
self._current_capability = data.get("current_capability", "")
|
|
23
33
|
|
|
24
34
|
self._id = data.get("_id", "")
|
|
25
35
|
self._zone_type = data.get("zone_type", "")
|
|
@@ -49,14 +59,69 @@ class QolsysZone(QolsysObservable):
|
|
|
49
59
|
self._created_by = data.get("created_by", "")
|
|
50
60
|
self._updated_by = data.get("updated_by", "")
|
|
51
61
|
self._updated_date = data.get("updated_date", "")
|
|
52
|
-
self._shortID = data.get("shortID", "")
|
|
53
62
|
self._diag_24hr = data.get("diag_24hr", "")
|
|
54
|
-
self._device_capability = data.get("device_capability", "")
|
|
55
63
|
self._sub_type = data.get("sub_type", "")
|
|
56
64
|
self._powerg_manufacture_id = data.get("powerg_manufacture_id", "")
|
|
57
65
|
self._parent_node = data.get("parent_node", "")
|
|
58
66
|
self._extras = data.get("extras", "")
|
|
59
67
|
|
|
68
|
+
# EXTRA POWERG ATTRIBUTES
|
|
69
|
+
self._powerg_long_id = ""
|
|
70
|
+
self._powerg_status_data:str = ""
|
|
71
|
+
self._powerg_temperature:str = ""
|
|
72
|
+
self._powerg_light:str = ""
|
|
73
|
+
self._powerg_notification_period = ""
|
|
74
|
+
self._powerg_average_link_quality = ""
|
|
75
|
+
self._powerg_link_quality = ""
|
|
76
|
+
self._powerg_link_status = ""
|
|
77
|
+
self._powerg_battery_voltage = ""
|
|
78
|
+
|
|
79
|
+
def is_powerg_enabled(self) -> bool:
|
|
80
|
+
return self._current_capability == "PowerG"
|
|
81
|
+
|
|
82
|
+
def is_powerg_temperature_enabled(self) -> bool:
|
|
83
|
+
return self._powerg_temperature != ""
|
|
84
|
+
|
|
85
|
+
def is_powerg_light_enabled(self) -> bool:
|
|
86
|
+
return self._powerg_light != ""
|
|
87
|
+
|
|
88
|
+
def update_powerg(self, data: dict) -> None:
|
|
89
|
+
short_id_update = data.get("shortID", "")
|
|
90
|
+
if short_id_update != self.shortID:
|
|
91
|
+
LOGGER.error("Updating Zone%s PowerG Attribute (%s) with Zone%s (different shortID)", self._zone_id, self.sensorname, short_id_update)
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
self.start_batch_update()
|
|
95
|
+
|
|
96
|
+
if "longID" in data:
|
|
97
|
+
self._powerg_long_id = data.get("longID")
|
|
98
|
+
|
|
99
|
+
if "status_data" in data:
|
|
100
|
+
self.powerg_status_data = data.get("status_data")
|
|
101
|
+
|
|
102
|
+
if "temperature" in data:
|
|
103
|
+
self.powerg_temperature = data.get("temperature")
|
|
104
|
+
|
|
105
|
+
if "light" in data:
|
|
106
|
+
self.powerg_light = data.get("light")
|
|
107
|
+
|
|
108
|
+
if "notification_period" in data:
|
|
109
|
+
self._powerg_notification_period = data.get("notification_period")
|
|
110
|
+
|
|
111
|
+
if "average_link_quality" in data:
|
|
112
|
+
self._powerg_average_link_quality = data.get("average_link_quality")
|
|
113
|
+
|
|
114
|
+
if "link_quality" in data:
|
|
115
|
+
self._powerg_link_quality = data.get("link_quality")
|
|
116
|
+
|
|
117
|
+
if "link_status" in data:
|
|
118
|
+
self._powerg_link_status = data.get("link_status")
|
|
119
|
+
|
|
120
|
+
if "battery_voltage" in data:
|
|
121
|
+
self._powerg_battery_voltage = data.get("battery_voltage")
|
|
122
|
+
|
|
123
|
+
self.end_batch_update()
|
|
124
|
+
|
|
60
125
|
def update(self, data: dict) -> None: # noqa: C901, PLR0912, PLR0915
|
|
61
126
|
|
|
62
127
|
zone_id_update = data.get("zoneid", "")
|
|
@@ -66,31 +131,24 @@ class QolsysZone(QolsysObservable):
|
|
|
66
131
|
|
|
67
132
|
self.start_batch_update()
|
|
68
133
|
|
|
69
|
-
# Update sensor_name
|
|
70
134
|
if "sensorname" in data:
|
|
71
135
|
self.sensorname = data.get("sensorname")
|
|
72
136
|
|
|
73
|
-
# Update sensorsatus
|
|
74
137
|
if "sensorstatus" in data:
|
|
75
138
|
self.sensorstatus = ZoneStatus(data.get("sensorstatus"))
|
|
76
139
|
|
|
77
|
-
# Update battery_status
|
|
78
140
|
if "battery_status" in data:
|
|
79
141
|
self.battery_status = data.get("battery_status")
|
|
80
142
|
|
|
81
|
-
# Update time
|
|
82
143
|
if "time" in data:
|
|
83
144
|
self.time = data.get("time")
|
|
84
145
|
|
|
85
|
-
# Update partition_id
|
|
86
146
|
if "partition_id" in data:
|
|
87
147
|
self._partition_id = data.get("partition_id")
|
|
88
148
|
|
|
89
|
-
# Update lastestdBm
|
|
90
149
|
if "lastestdBm" in data:
|
|
91
150
|
self.latestdBm = data.get("latestdBm")
|
|
92
151
|
|
|
93
|
-
# Update averagedBm
|
|
94
152
|
if "averagedBm" in data:
|
|
95
153
|
self.averagedBm = data.get("averagedBm")
|
|
96
154
|
|
|
@@ -198,10 +256,18 @@ class QolsysZone(QolsysObservable):
|
|
|
198
256
|
def partition_id(self) -> str:
|
|
199
257
|
return self._partition_id
|
|
200
258
|
|
|
259
|
+
@property
|
|
260
|
+
def shortID(self) -> str:
|
|
261
|
+
return self._shortID
|
|
262
|
+
|
|
201
263
|
@property
|
|
202
264
|
def time(self) -> str:
|
|
203
265
|
return self._time
|
|
204
266
|
|
|
267
|
+
@property
|
|
268
|
+
def current_capability(self) -> str:
|
|
269
|
+
return self._current_capability
|
|
270
|
+
|
|
205
271
|
@property
|
|
206
272
|
def latestdBm(self) -> str:
|
|
207
273
|
return self._latestdBm
|
|
@@ -210,25 +276,83 @@ class QolsysZone(QolsysObservable):
|
|
|
210
276
|
def averagedBm(self) -> str:
|
|
211
277
|
return self._averagedBm
|
|
212
278
|
|
|
279
|
+
@property
|
|
280
|
+
def device_capability(self) -> str:
|
|
281
|
+
return self._device_capability
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def powerg_temperature(self) -> float | None:
|
|
285
|
+
return self._powerg_temperature
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def powerg_light(self) -> float | None:
|
|
289
|
+
return self._powerg_light
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def powerg_status_data(self) -> str:
|
|
293
|
+
return self._powerg_status_data
|
|
294
|
+
|
|
295
|
+
@powerg_temperature.setter
|
|
296
|
+
def powerg_temperature(self, value: str) -> None:
|
|
297
|
+
if self._powerg_temperature != value:
|
|
298
|
+
LOGGER.debug("Zone%s (%s) - powerg_temperature: %s", self._zone_id, self.sensorname, value)
|
|
299
|
+
self._powerg_temperature = value
|
|
300
|
+
self.notify()
|
|
301
|
+
|
|
302
|
+
@powerg_light.setter
|
|
303
|
+
def powerg_light(self, value: str) -> None:
|
|
304
|
+
if self._powerg_light != value:
|
|
305
|
+
LOGGER.debug("Zone%s (%s) - powerg_light: %s", self._zone_id, self.sensorname, value)
|
|
306
|
+
self._powerg_light = value
|
|
307
|
+
self.notify()
|
|
308
|
+
|
|
309
|
+
@powerg_status_data.setter
|
|
310
|
+
def powerg_status_data(self, value: str) -> None:
|
|
311
|
+
if self._powerg_status_data != value:
|
|
312
|
+
LOGGER.debug("Zone%s (%s) - powerg_status_data: %s", self._zone_id, self.sensorname, value)
|
|
313
|
+
self._powerg_status_data = value
|
|
314
|
+
self.notify()
|
|
315
|
+
|
|
213
316
|
@averagedBm.setter
|
|
214
317
|
def averagedBm(self, value: str) -> None:
|
|
215
318
|
if self._averagedBm != value:
|
|
216
319
|
self._averagedBm = value
|
|
320
|
+
|
|
321
|
+
if value == "":
|
|
322
|
+
self._averagedBm = 0
|
|
323
|
+
|
|
217
324
|
self.notify()
|
|
218
325
|
|
|
219
326
|
@latestdBm.setter
|
|
220
327
|
def latestdBm(self, value: str) -> None:
|
|
221
328
|
if self._latestdBm != value:
|
|
222
329
|
self.latestdBm = value
|
|
330
|
+
|
|
331
|
+
if value == "":
|
|
332
|
+
self._latestdBm = 0
|
|
333
|
+
|
|
223
334
|
self.notify()
|
|
224
335
|
|
|
225
336
|
@sensorstatus.setter
|
|
226
337
|
def sensorstatus(self, new_value: ZoneStatus) -> None:
|
|
338
|
+
if self._settings.motion_sensor_delay and self._sensortype in {ZoneSensorType.MOTION, ZoneSensorType.PANEL_MOTION}:
|
|
339
|
+
if new_value == ZoneStatus.IDLE:
|
|
340
|
+
return
|
|
341
|
+
if self._delay_task is not None:
|
|
342
|
+
self._delay_task.cancel()
|
|
343
|
+
self._delay_task = asyncio.create_task(self.delay_zone(ZoneStatus.IDLE))
|
|
344
|
+
|
|
227
345
|
if self._sensorstatus != new_value:
|
|
228
346
|
LOGGER.debug("Zone%s (%s) - sensorstatus: %s", self._zone_id, self.sensorname, new_value)
|
|
229
347
|
self._sensorstatus = new_value
|
|
230
348
|
self.notify()
|
|
231
349
|
|
|
350
|
+
async def delay_zone(self,next_status: ZoneStatus) -> None:
|
|
351
|
+
await asyncio.sleep(self._settings.motion_sensor_delay_sec)
|
|
352
|
+
self._sensorstatus = next_status
|
|
353
|
+
LOGGER.debug("Zone%s (%s) - sensorstatus: %s", self._zone_id, self.sensorname, next_status)
|
|
354
|
+
self.notify()
|
|
355
|
+
|
|
232
356
|
@battery_status.setter
|
|
233
357
|
def battery_status(self, value: str) -> None:
|
|
234
358
|
if self._battery_status != value:
|
|
@@ -275,6 +399,20 @@ class QolsysZone(QolsysObservable):
|
|
|
275
399
|
self.partition_id = value
|
|
276
400
|
self.notify()
|
|
277
401
|
|
|
402
|
+
def to_powerg_dict(self) -> dict:
|
|
403
|
+
return {
|
|
404
|
+
"shortID": self.shortID,
|
|
405
|
+
"longID": self._powerg_long_id,
|
|
406
|
+
"status_data": self._powerg_status_data,
|
|
407
|
+
"temperature": self._powerg_temperature,
|
|
408
|
+
"light": self._powerg_light,
|
|
409
|
+
"notification_period": self._powerg_notification_period,
|
|
410
|
+
"average_link_quality": self._powerg_average_link_quality,
|
|
411
|
+
"link_quality": self._powerg_link_quality,
|
|
412
|
+
"link_status": self._powerg_link_status,
|
|
413
|
+
"battery_voltage": self._powerg_battery_voltage,
|
|
414
|
+
}
|
|
415
|
+
|
|
278
416
|
def to_dict(self) -> dict:
|
|
279
417
|
return {
|
|
280
418
|
"_id": self.id,
|