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.

@@ -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) -> str:
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
- @property
87
- def mqtt_timeout(self) -> int:
88
- return self._mqtt_timeout
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, ping:int) -> None:
96
- self._mqtt_ping = 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)
@@ -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 .partition import QolsysPartition
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: C901, PLR0912
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(int(state_zwave.node_id))
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(int(state_scene.scene_id))
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: # noqa: C901
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(int(state_zone.zone_id))
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: # noqa: C901
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) - current_temp: %s", zid, name, zwave.lock_status)
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,