qolsys-controller 0.0.34__tar.gz → 0.0.35__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.
Potentially problematic release.
This version of qolsys-controller might be problematic. Click here for more details.
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/PKG-INFO +5 -4
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/README.md +4 -3
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/pyproject.toml +1 -1
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/enum.py +1 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/panel.py +10 -2
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/plugin_remote.py +44 -61
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/settings.py +18 -1
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/state.py +8 -4
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/task_manager.py +8 -3
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zone.py +29 -1
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/.github/workflows/build.yml +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/.github/workflows/publish.yml +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/.gitignore +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/Info_mqtt.md +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/LICENSE +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/bin/qolsys.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/example.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/info_pairing.md +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/info_qolsys.md +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/__init__.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/controller.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/db.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_alarmedsensor.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_automation.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_country_locale.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_dashboard_msgs.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_dimmerlight.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_doorlock.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_eu_event.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_heat_map.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_history.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_iqremotesettings.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_iqrouter_network_config.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_iqrouter_user_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_master_slave.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_nest_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_output_rules.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_partition.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_pgm_outputs.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_powerg_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_qolsyssettings.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_scene.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_sensor.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_shades.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_smartsocket.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_state.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_tcc.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_thermostat.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_trouble_conditions.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_user.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_virtual_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_weather.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zigbee_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zwave_association_group.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zwave_history.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zwave_node.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zwave_other.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/enum_zwave.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/errors.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/mdns.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/mqtt_command_queue.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/observable.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/partition.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/pki.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/plugin.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/plugin_c4.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/scene.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/utils_mqtt.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_device.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_dimmer.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_garagedoor.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_generic.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_lock.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_outlet.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/zwave_thermostat.py +0 -0
- {qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/requirements.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qolsys-controller
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.35
|
|
4
4
|
Summary: A Python module that emulates a virtual IQ Remote device, enabling full local control of a Qolsys IQ Panel
|
|
5
5
|
Project-URL: Homepage, https://github.com/EHylands/QolsysController
|
|
6
6
|
Project-URL: Issues, https://github.com/EHylands/QolsysController/issues
|
|
@@ -33,10 +33,11 @@ A Python module that emulates a virtual IQ Remote device, enabling full **local
|
|
|
33
33
|
| **Panel** | Diagnostics sensors | ✅ |
|
|
34
34
|
|---|---|---|
|
|
35
35
|
| **Partitions** | Arming status | ✅ |
|
|
36
|
-
| | Set Exit sounds | ✅ |
|
|
37
|
-
| | Home Instant arming | ✅ |
|
|
38
|
-
| | Home Silent Disarm | ✅ |
|
|
39
36
|
| | Alarm state and type | ✅ |
|
|
37
|
+
| | Set Exit sound | ✅ |
|
|
38
|
+
| | Set Entry Delay | ✅ |
|
|
39
|
+
| | Arm-Stay Instant arming | ✅ |
|
|
40
|
+
| | Arm-Stay Silent Disarm | ✅ |
|
|
40
41
|
| | Disarm pictures | 🛠️ WIP |
|
|
41
42
|
|---|---|---|
|
|
42
43
|
| **Zones** | Sensor Status | ✅ |
|
|
@@ -17,10 +17,11 @@ A Python module that emulates a virtual IQ Remote device, enabling full **local
|
|
|
17
17
|
| **Panel** | Diagnostics sensors | ✅ |
|
|
18
18
|
|---|---|---|
|
|
19
19
|
| **Partitions** | Arming status | ✅ |
|
|
20
|
-
| | Set Exit sounds | ✅ |
|
|
21
|
-
| | Home Instant arming | ✅ |
|
|
22
|
-
| | Home Silent Disarm | ✅ |
|
|
23
20
|
| | Alarm state and type | ✅ |
|
|
21
|
+
| | Set Exit sound | ✅ |
|
|
22
|
+
| | Set Entry Delay | ✅ |
|
|
23
|
+
| | Arm-Stay Instant arming | ✅ |
|
|
24
|
+
| | Arm-Stay Silent Disarm | ✅ |
|
|
24
25
|
| | Disarm pictures | 🛠️ WIP |
|
|
25
26
|
|---|---|---|
|
|
26
27
|
| **Zones** | Sensor Status | ✅ |
|
|
@@ -26,6 +26,7 @@ class PartitionAlarmType(StrEnum):
|
|
|
26
26
|
SILENT_AUXILIARY_EMERGENCY = "Silent Auxiliary Emergency"
|
|
27
27
|
SILENT_POLICE_EMERGENCY = "Silent Police Emergency"
|
|
28
28
|
GLASS_BREAK_AWAY_ONLY = "glassbreakawayonly"
|
|
29
|
+
GLASS_BREAK = "glassbreak"
|
|
29
30
|
|
|
30
31
|
class ZoneStatus(StrEnum):
|
|
31
32
|
ALARMED = "Alarmed"
|
|
@@ -353,7 +353,7 @@ class QolsysPanel(QolsysObservable):
|
|
|
353
353
|
def parse_iq2meid_message(self, data: dict) -> bool: # noqa: C901, PLR0912, PLR0915
|
|
354
354
|
|
|
355
355
|
eventName = data.get("eventName")
|
|
356
|
-
dbOperation = data.get("dbOperation")
|
|
356
|
+
dbOperation = data.get("dbOperation","")
|
|
357
357
|
uri = data.get("uri")
|
|
358
358
|
|
|
359
359
|
match eventName:
|
|
@@ -361,6 +361,9 @@ class QolsysPanel(QolsysObservable):
|
|
|
361
361
|
case "stopScreenCapture":
|
|
362
362
|
pass
|
|
363
363
|
|
|
364
|
+
case "primaryDisconnect":
|
|
365
|
+
LOGGER.info("Main Panel Disconnect")
|
|
366
|
+
|
|
364
367
|
case "dbChanged":
|
|
365
368
|
|
|
366
369
|
match dbOperation:
|
|
@@ -508,6 +511,11 @@ class QolsysPanel(QolsysObservable):
|
|
|
508
511
|
if scene is not None and isinstance(node, QolsysScene):
|
|
509
512
|
scene.update(content_values)
|
|
510
513
|
|
|
514
|
+
# Update Trouble Conditions
|
|
515
|
+
case self.db.table_trouble_conditions:
|
|
516
|
+
self.db.table_trouble_conditions.update(selection,selection_argument,content_values)
|
|
517
|
+
# No action needed
|
|
518
|
+
|
|
511
519
|
|
|
512
520
|
case _:
|
|
513
521
|
LOGGER.debug("iq2meid updating unknow uri:%s", uri)
|
|
@@ -799,7 +807,7 @@ class QolsysPanel(QolsysObservable):
|
|
|
799
807
|
|
|
800
808
|
# Create sensors array
|
|
801
809
|
for zone_info in zones_list:
|
|
802
|
-
zones.append(QolsysZone(zone_info))
|
|
810
|
+
zones.append(QolsysZone(zone_info,self._settings))
|
|
803
811
|
|
|
804
812
|
return zones
|
|
805
813
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import base64
|
|
3
|
+
import contextlib
|
|
3
4
|
import datetime
|
|
4
5
|
import json
|
|
5
6
|
import logging
|
|
6
7
|
import random
|
|
7
8
|
import ssl
|
|
8
9
|
import uuid
|
|
10
|
+
from pathlib import Path
|
|
9
11
|
|
|
10
12
|
import aiomqtt
|
|
11
13
|
|
|
@@ -146,7 +148,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
146
148
|
return True
|
|
147
149
|
|
|
148
150
|
async def start_operation(self) -> None:
|
|
149
|
-
await self._task_manager.run(self.mqtt_connect_task(reconnect=True), self._mqtt_task_connect_label)
|
|
151
|
+
await self._task_manager.run(self.mqtt_connect_task(reconnect=True, run_forever=True), self._mqtt_task_connect_label)
|
|
150
152
|
|
|
151
153
|
async def stop_operation(self) -> None:
|
|
152
154
|
LOGGER.debug("Stopping Plugin Operation")
|
|
@@ -166,7 +168,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
166
168
|
self.connected = False
|
|
167
169
|
self.connected_observer.notify()
|
|
168
170
|
|
|
169
|
-
async def mqtt_connect_task(self, reconnect: bool) -> None:
|
|
171
|
+
async def mqtt_connect_task(self, reconnect: bool, run_forever: bool) -> None:
|
|
170
172
|
# Configure TLS parameters for MQTT connection
|
|
171
173
|
tls_params = aiomqtt.TLSParameters(
|
|
172
174
|
ca_certs=self._pki.qolsys_cer_file_path,
|
|
@@ -179,6 +181,9 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
179
181
|
|
|
180
182
|
LOGGER.debug("MQTT: Connecting ...")
|
|
181
183
|
|
|
184
|
+
self._task_manager.cancel(self._mqtt_task_listen_label)
|
|
185
|
+
self._task_manager.cancel(self._mqtt_task_ping_label)
|
|
186
|
+
|
|
182
187
|
while True:
|
|
183
188
|
try:
|
|
184
189
|
self.aiomqtt = aiomqtt.Client(
|
|
@@ -188,7 +193,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
188
193
|
tls_insecure=True,
|
|
189
194
|
clean_session=True,
|
|
190
195
|
timeout=self.settings.mqtt_timeout,
|
|
191
|
-
identifier=
|
|
196
|
+
identifier= self.settings.mqtt_remote_client_id,
|
|
192
197
|
)
|
|
193
198
|
|
|
194
199
|
await self.aiomqtt.__aenter__()
|
|
@@ -207,14 +212,11 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
207
212
|
# Only log all traffic for debug purposes
|
|
208
213
|
if self.log_mqtt_mesages:
|
|
209
214
|
# Subscribe to MQTT commands send to panel by other devices
|
|
210
|
-
|
|
215
|
+
await self.aiomqtt.subscribe("mastermeid", qos=self.settings.mqtt_qos)
|
|
211
216
|
|
|
212
217
|
# Subscribe to all topics
|
|
213
|
-
await self.aiomqtt.subscribe("#", qos=self.settings.mqtt_qos)
|
|
218
|
+
# await self.aiomqtt.subscribe("#", qos=self.settings.mqtt_qos)
|
|
214
219
|
|
|
215
|
-
# Start mqtt_listent_task and mqtt_ping_task
|
|
216
|
-
self._task_manager.cancel(self._mqtt_task_listen_label)
|
|
217
|
-
self._task_manager.cancel(self._mqtt_task_ping_label)
|
|
218
220
|
self._task_manager.run(self.mqtt_listen_task(), self._mqtt_task_listen_label)
|
|
219
221
|
self._task_manager.run(self.mqtt_ping_task(), self._mqtt_task_ping_label)
|
|
220
222
|
|
|
@@ -223,7 +225,6 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
223
225
|
self.panel.product_type = response_connect.get("primary_product_type", "")
|
|
224
226
|
|
|
225
227
|
await self.command_pingevent()
|
|
226
|
-
await self.command_timesync()
|
|
227
228
|
await self.command_pair_status_request()
|
|
228
229
|
|
|
229
230
|
response_database = await self.command_sync_database()
|
|
@@ -234,17 +235,24 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
234
235
|
|
|
235
236
|
self.connected = True
|
|
236
237
|
self.connected_observer.notify()
|
|
238
|
+
|
|
239
|
+
if not run_forever:
|
|
240
|
+
self.connected = False
|
|
241
|
+
self.connected_observer.notify()
|
|
242
|
+
self._task_manager.cancel(self._mqtt_task_listen_label)
|
|
243
|
+
self._task_manager.cancel(self._mqtt_task_ping_label)
|
|
244
|
+
await self.aiomqtt.__aexit__(None,None,None)
|
|
245
|
+
|
|
237
246
|
break
|
|
238
247
|
|
|
239
248
|
except aiomqtt.MqttError as err:
|
|
240
249
|
# Receive pannel network error
|
|
241
250
|
self.connected = False
|
|
242
251
|
self.connected_observer.notify()
|
|
243
|
-
await self.aiomqtt.__aexit__(None, None, None)
|
|
244
252
|
self.aiomqtt = None
|
|
245
253
|
|
|
246
254
|
if reconnect:
|
|
247
|
-
LOGGER.debug("MQTT Error - %s: Reconnecting in %s seconds ...", err, self.settings.mqtt_timeout)
|
|
255
|
+
LOGGER.debug("MQTT Error - %s: Connect - Reconnecting in %s seconds ...", err, self.settings.mqtt_timeout)
|
|
248
256
|
await asyncio.sleep(self.settings.mqtt_timeout)
|
|
249
257
|
else:
|
|
250
258
|
raise QolsysMqttError from err
|
|
@@ -255,14 +263,14 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
255
263
|
# Pannels need to be re-paired
|
|
256
264
|
self.connected = False
|
|
257
265
|
self.connected_observer.notify()
|
|
258
|
-
await self.aiomqtt.__aexit__(None, None, None)
|
|
259
266
|
self.aiomqtt = None
|
|
260
267
|
raise QolsysSslError from err
|
|
261
268
|
|
|
262
269
|
async def mqtt_ping_task(self) -> None:
|
|
263
270
|
while True:
|
|
264
271
|
if self.aiomqtt is not None and self.connected:
|
|
265
|
-
|
|
272
|
+
with contextlib.suppress(aiomqtt.MqttError):
|
|
273
|
+
await self.command_pingevent()
|
|
266
274
|
|
|
267
275
|
await asyncio.sleep(self.settings.mqtt_ping)
|
|
268
276
|
|
|
@@ -293,25 +301,13 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
293
301
|
decoded_payload = base64.b64decode(zwave.get("ZWAVE_PAYLOAD","")).hex()
|
|
294
302
|
LOGGER.debug("Z-Wave Response: Node(%s) - Status(%s) - Payload(%s)",zwave.get("NODE_ID",""),zwave.get("ZWAVE_COMMAND_STATUS",""),decoded_payload)
|
|
295
303
|
|
|
296
|
-
|
|
297
304
|
except aiomqtt.MqttError as err:
|
|
298
305
|
self.connected = False
|
|
299
306
|
self.connected_observer.notify()
|
|
300
307
|
|
|
301
|
-
LOGGER.debug("%s: Reconnecting in %s seconds ...", err, self.settings.mqtt_timeout)
|
|
302
|
-
self._task_manager.cancel(self.mqtt_ping_task)
|
|
308
|
+
LOGGER.debug("%s: Listen - Reconnecting in %s seconds ...", err, self.settings.mqtt_timeout)
|
|
303
309
|
await asyncio.sleep(self.settings.mqtt_timeout)
|
|
304
|
-
self._task_manager.run(self.mqtt_connect_task(reconnect=True), self._mqtt_task_connect_label)
|
|
305
|
-
|
|
306
|
-
except ssl.SSLError as err:
|
|
307
|
-
# SSL error is and authentication error with invalid certificates en pki
|
|
308
|
-
# We cannot recover from this error automaticly
|
|
309
|
-
# Pannels need to be re-paired
|
|
310
|
-
self.connected = False
|
|
311
|
-
self.connected_observer.notify()
|
|
312
|
-
await self.aiomqtt.__aexit__(None, None, None)
|
|
313
|
-
self.aiomqtt = None
|
|
314
|
-
raise QolsysSslError from err
|
|
310
|
+
self._task_manager.run(self.mqtt_connect_task(reconnect=True, run_forever=True), self._mqtt_task_connect_label)
|
|
315
311
|
|
|
316
312
|
async def start_initial_pairing(self) -> bool:
|
|
317
313
|
# check if random_mac exist
|
|
@@ -354,7 +350,6 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
354
350
|
context.load_cert_chain(certfile=self._pki.cer_file_path, keyfile=self._pki.key_file_path)
|
|
355
351
|
self.certificate_exchange_server = await asyncio.start_server(self.handle_key_exchange_client,
|
|
356
352
|
self.settings.plugin_ip, pairing_port, ssl=context)
|
|
357
|
-
|
|
358
353
|
LOGGER.debug("Certificate Exchange Server Waiting for Panel")
|
|
359
354
|
LOGGER.debug("Press Pair Button in IQ Remote Config Page ...")
|
|
360
355
|
|
|
@@ -371,7 +366,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
371
366
|
|
|
372
367
|
# We have client sgined certificate at this point
|
|
373
368
|
# Connect to Panel MQTT to send pairing command
|
|
374
|
-
await self._task_manager.run(self.mqtt_connect_task(reconnect=False), self._mqtt_task_connect_label)
|
|
369
|
+
await self._task_manager.run(self.mqtt_connect_task(reconnect=False, run_forever=False), self._mqtt_task_connect_label)
|
|
375
370
|
LOGGER.debug("Plugin Pairing Completed ")
|
|
376
371
|
return True
|
|
377
372
|
|
|
@@ -381,9 +376,6 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
381
376
|
received_signed_client_certificate = False
|
|
382
377
|
received_qolsys_cer = False
|
|
383
378
|
|
|
384
|
-
signed_certificate_data = ""
|
|
385
|
-
qolsys_certificate_data = ""
|
|
386
|
-
|
|
387
379
|
try:
|
|
388
380
|
continue_pairing = True
|
|
389
381
|
while continue_pairing:
|
|
@@ -410,7 +402,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
410
402
|
await writer.drain()
|
|
411
403
|
|
|
412
404
|
# Sending CSR File to panel
|
|
413
|
-
with open(self._pki.csr_file_path, "rb") as file:
|
|
405
|
+
with Path.open(self._pki.csr_file_path, "rb") as file:
|
|
414
406
|
content = file.read()
|
|
415
407
|
LOGGER.debug("Sending to Panel: [CSR File Content]")
|
|
416
408
|
writer.write(content)
|
|
@@ -421,36 +413,26 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
421
413
|
|
|
422
414
|
# Read signed certificate data
|
|
423
415
|
if (received_panel_mac and not received_signed_client_certificate and not received_qolsys_cer):
|
|
416
|
+
request = await reader.readuntil(b"sent")
|
|
417
|
+
if request.endswith(b"sent"):
|
|
418
|
+
request = request[:-5]
|
|
424
419
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
certificates = [item for item in signed_certificate_data.split("sent") if item]
|
|
430
|
-
LOGGER.debug("Saving [Signed Client Certificate]")
|
|
431
|
-
with open(self._pki.secure_file_path, "wb") as f:
|
|
432
|
-
f.write(certificates[0].encode())
|
|
433
|
-
received_signed_client_certificate = True
|
|
434
|
-
|
|
435
|
-
if len(certificates) > 1:
|
|
436
|
-
qolsys_certificate_data += certificates[1]
|
|
420
|
+
LOGGER.debug("Saving [Signed Client Certificate]")
|
|
421
|
+
with Path.open(self._pki.secure_file_path, "wb") as f:
|
|
422
|
+
f.write(request)
|
|
423
|
+
received_signed_client_certificate = True
|
|
437
424
|
|
|
438
425
|
# Read qolsys certificate data
|
|
439
426
|
if (received_panel_mac and received_signed_client_certificate and not received_qolsys_cer):
|
|
440
|
-
|
|
441
|
-
request
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
427
|
+
request = await reader.readuntil(b"sent")
|
|
428
|
+
if request.endswith(b"sent"):
|
|
429
|
+
request = request[:-5]
|
|
430
|
+
|
|
431
|
+
LOGGER.debug("Saving [Qolsys Certificate]")
|
|
432
|
+
with Path.open(self._pki.qolsys_cer_file_path, "wb") as f:
|
|
433
|
+
f.write(request)
|
|
434
|
+
received_qolsys_cer = True
|
|
445
435
|
continue_pairing = False
|
|
446
|
-
certificates = [item for item in qolsys_certificate_data.split("sent") if item]
|
|
447
|
-
|
|
448
|
-
LOGGER.debug("Saving [Qolsys Certificate]")
|
|
449
|
-
with open(self._pki.qolsys_cer_file_path, "w") as f:
|
|
450
|
-
f.write(certificates[0])
|
|
451
|
-
received_qolsys_cer = True
|
|
452
|
-
continue_pairing = False
|
|
453
|
-
writer.close()
|
|
454
436
|
|
|
455
437
|
continue
|
|
456
438
|
|
|
@@ -462,6 +444,7 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
462
444
|
|
|
463
445
|
finally:
|
|
464
446
|
writer.close()
|
|
447
|
+
await writer.wait_closed()
|
|
465
448
|
self.certificate_exchange_server.close()
|
|
466
449
|
|
|
467
450
|
async def send_command(self, topic: str, json_payload: str, request_id: str) -> dict:
|
|
@@ -1185,11 +1168,11 @@ class QolsysPluginRemote(QolsysPlugin):
|
|
|
1185
1168
|
LOGGER.debug("MQTT:Receiving zwave_switch_binary command")
|
|
1186
1169
|
|
|
1187
1170
|
|
|
1188
|
-
async def command_arm(self, partition_id: str, arming_type: str, user_code: str = "", exit_sounds: bool = False,
|
|
1171
|
+
async def command_arm(self, partition_id: str, arming_type: str, user_code: str = "", exit_sounds: bool = False, # noqa: PLR0913
|
|
1189
1172
|
instant_arm: bool = False, entry_delay: bool = True) -> bool:
|
|
1190
1173
|
|
|
1191
|
-
LOGGER.debug("MQTT: Sending arm command: partition%s, arming_type:%s,
|
|
1192
|
-
partition_id, arming_type,
|
|
1174
|
+
LOGGER.debug("MQTT: Sending arm command: partition%s, arming_type:%s, exit_sounds:%s, instant_arm: %s, entry_delay:%s",
|
|
1175
|
+
partition_id, arming_type,exit_sounds,instant_arm,entry_delay)
|
|
1193
1176
|
|
|
1194
1177
|
user_id = 0
|
|
1195
1178
|
|
|
@@ -14,6 +14,7 @@ class QolsysSettings:
|
|
|
14
14
|
self._panel_mac = ""
|
|
15
15
|
self._panel_ip = ""
|
|
16
16
|
|
|
17
|
+
# Path
|
|
17
18
|
self._config_directory: Path = Path()
|
|
18
19
|
self._pki_directory: Path = Path()
|
|
19
20
|
self._media_directory: Path = Path()
|
|
@@ -28,6 +29,10 @@ class QolsysSettings:
|
|
|
28
29
|
self._mqtt_qos:int = 0
|
|
29
30
|
self._mqtt_remote_client_id = ""
|
|
30
31
|
|
|
32
|
+
# Operation
|
|
33
|
+
self._motion_sensor_delay:bool = True
|
|
34
|
+
self._motion_sensor_delay_sec:int = 310
|
|
35
|
+
|
|
31
36
|
@property
|
|
32
37
|
def random_mac(self) -> str:
|
|
33
38
|
return self._random_mac
|
|
@@ -60,6 +65,18 @@ class QolsysSettings:
|
|
|
60
65
|
def mqtt_ping(self) -> int:
|
|
61
66
|
return self._mqtt_ping
|
|
62
67
|
|
|
68
|
+
@property
|
|
69
|
+
def motion_sensor_delay(self) -> bool:
|
|
70
|
+
return self._motion_sensor_delay
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def motion_sensor_delay_sec(self) -> int:
|
|
74
|
+
return self._motion_sensor_delay_sec
|
|
75
|
+
|
|
76
|
+
@motion_sensor_delay.setter
|
|
77
|
+
def motion_sensor_delay(self, value: bool) -> None:
|
|
78
|
+
self._motion_sensor_delay = value
|
|
79
|
+
|
|
63
80
|
@panel_ip.setter
|
|
64
81
|
def panel_ip(self, panel_ip: str) -> None:
|
|
65
82
|
self._panel_ip = panel_ip
|
|
@@ -131,7 +148,7 @@ class QolsysSettings:
|
|
|
131
148
|
LOGGER.debug("Found Plugin IP: %s", self._plugin_ip)
|
|
132
149
|
return True
|
|
133
150
|
|
|
134
|
-
def check_config_directory(self, create: bool = True) -> bool:
|
|
151
|
+
def check_config_directory(self, create: bool = True) -> bool: # noqa: PLR0911
|
|
135
152
|
if not self.config_directory.is_dir():
|
|
136
153
|
if not create:
|
|
137
154
|
LOGGER.debug("config_directory not found: %s", self.config_directory)
|
|
@@ -102,6 +102,7 @@ class QolsysState(QolsysObservable):
|
|
|
102
102
|
return
|
|
103
103
|
|
|
104
104
|
self.partitions.append(new_partition)
|
|
105
|
+
self.partitions.sort(key=lambda x: x.id, reverse=False)
|
|
105
106
|
self.state_partition_observer.notify()
|
|
106
107
|
|
|
107
108
|
def partition_delete(self, partition_id: str) -> None:
|
|
@@ -128,6 +129,8 @@ class QolsysState(QolsysObservable):
|
|
|
128
129
|
return
|
|
129
130
|
|
|
130
131
|
self.scenes.append(new_scene)
|
|
132
|
+
self.scenes.sort(key=lambda x: x.scene_id, reverse=False)
|
|
133
|
+
|
|
131
134
|
self.state_scene_observer.notify()
|
|
132
135
|
|
|
133
136
|
def scene_delete(self, scene_id: str) -> None:
|
|
@@ -144,7 +147,6 @@ class QolsysState(QolsysObservable):
|
|
|
144
147
|
for zone in self.zones:
|
|
145
148
|
if zone.zone_id == zone_id:
|
|
146
149
|
return zone
|
|
147
|
-
|
|
148
150
|
return None
|
|
149
151
|
|
|
150
152
|
def zone_add(self, new_zone: QolsysZone) -> None:
|
|
@@ -154,6 +156,7 @@ class QolsysState(QolsysObservable):
|
|
|
154
156
|
return
|
|
155
157
|
|
|
156
158
|
self.zones.append(new_zone)
|
|
159
|
+
self.zones.sort(key=lambda x: x.zone_id, reverse=False)
|
|
157
160
|
self.state_zone_observer.notify()
|
|
158
161
|
|
|
159
162
|
def zone_delete(self, zone_id: str) -> None:
|
|
@@ -181,6 +184,7 @@ class QolsysState(QolsysObservable):
|
|
|
181
184
|
return
|
|
182
185
|
|
|
183
186
|
self.zwave_devices.append(new_zwave)
|
|
187
|
+
self.zwave_devices.sort(key=lambda x: x.node_id, reverse=False)
|
|
184
188
|
self.state_zwave_observer.notify()
|
|
185
189
|
|
|
186
190
|
def zwave_delete(self, node_id: str) -> None:
|
|
@@ -247,7 +251,7 @@ class QolsysState(QolsysObservable):
|
|
|
247
251
|
for state_zwave in self.zwave_devices:
|
|
248
252
|
if state_zwave.node_id not in db_zwave_list:
|
|
249
253
|
LOGGER.debug("sync_data - delete ZWave%s", state_zwave.none_id)
|
|
250
|
-
self.zwave_delete(
|
|
254
|
+
self.zwave_delete(state_zwave.node_id)
|
|
251
255
|
|
|
252
256
|
def sync_scenes_data(self, db_scenes: list[QolsysScene]) -> None:
|
|
253
257
|
db_scene_list = []
|
|
@@ -271,7 +275,7 @@ class QolsysState(QolsysObservable):
|
|
|
271
275
|
for state_scene in self.scenes:
|
|
272
276
|
if state_scene.scene_id not in db_scene_list:
|
|
273
277
|
LOGGER.debug("sync_data - delete Scene%s", state_scene.scene_id)
|
|
274
|
-
self.scene_delete(
|
|
278
|
+
self.scene_delete(state_scene.scene_id)
|
|
275
279
|
|
|
276
280
|
# Add new scene
|
|
277
281
|
for db_scene in db_scenes:
|
|
@@ -300,7 +304,7 @@ class QolsysState(QolsysObservable):
|
|
|
300
304
|
for state_zone in self.zones:
|
|
301
305
|
if state_zone.zone_id not in db_zone_list:
|
|
302
306
|
LOGGER.debug("sync_data - delete Zone%s", state_zone.zone_id)
|
|
303
|
-
self.zone_delete(
|
|
307
|
+
self.zone_delete(state_zone.zone_id)
|
|
304
308
|
|
|
305
309
|
# Add new zone
|
|
306
310
|
for db_zone in db_zones:
|
|
@@ -2,8 +2,6 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
from collections.abc import Coroutine
|
|
4
4
|
|
|
5
|
-
import aiomqtt
|
|
6
|
-
|
|
7
5
|
LOGGER = logging.getLogger(__name__)
|
|
8
6
|
|
|
9
7
|
|
|
@@ -31,14 +29,21 @@ class QolsysTaskManager:
|
|
|
31
29
|
task.add_done_callback(_done_callback)
|
|
32
30
|
return task
|
|
33
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
|
+
|
|
34
38
|
def cancel(self, label: str) -> None:
|
|
35
39
|
for task in self._tasks:
|
|
36
40
|
if task.get_name() == label:
|
|
37
41
|
task.cancel()
|
|
38
42
|
|
|
39
|
-
def cancel_all(self) -> None:
|
|
43
|
+
async def cancel_all(self) -> None:
|
|
40
44
|
for task in self._tasks:
|
|
41
45
|
task.cancel()
|
|
46
|
+
await task
|
|
42
47
|
|
|
43
48
|
async def wait_all(self) -> None:
|
|
44
49
|
if self._tasks:
|
|
@@ -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:
|
|
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", ""))
|
|
@@ -214,21 +221,42 @@ class QolsysZone(QolsysObservable):
|
|
|
214
221
|
def averagedBm(self, value: str) -> None:
|
|
215
222
|
if self._averagedBm != value:
|
|
216
223
|
self._averagedBm = value
|
|
224
|
+
|
|
225
|
+
if value == "":
|
|
226
|
+
self._averagedBm = 0
|
|
227
|
+
|
|
217
228
|
self.notify()
|
|
218
229
|
|
|
219
230
|
@latestdBm.setter
|
|
220
231
|
def latestdBm(self, value: str) -> None:
|
|
221
232
|
if self._latestdBm != value:
|
|
222
233
|
self.latestdBm = value
|
|
234
|
+
|
|
235
|
+
if value == "":
|
|
236
|
+
self._latestdBm = 0
|
|
237
|
+
|
|
223
238
|
self.notify()
|
|
224
239
|
|
|
225
240
|
@sensorstatus.setter
|
|
226
241
|
def sensorstatus(self, new_value: ZoneStatus) -> None:
|
|
242
|
+
if self._settings.motion_sensor_delay and self._sensortype in {ZoneSensorType.MOTION, ZoneSensorType.PANEL_MOTION}:
|
|
243
|
+
if new_value == ZoneStatus.IDLE:
|
|
244
|
+
return
|
|
245
|
+
if self._delay_task is not None:
|
|
246
|
+
self._delay_task.cancel()
|
|
247
|
+
self._delay_task = asyncio.create_task(self.delay_zone(ZoneStatus.IDLE))
|
|
248
|
+
|
|
227
249
|
if self._sensorstatus != new_value:
|
|
228
250
|
LOGGER.debug("Zone%s (%s) - sensorstatus: %s", self._zone_id, self.sensorname, new_value)
|
|
229
251
|
self._sensorstatus = new_value
|
|
230
252
|
self.notify()
|
|
231
253
|
|
|
254
|
+
async def delay_zone(self,next_status: ZoneStatus) -> None:
|
|
255
|
+
await asyncio.sleep(self._settings.motion_sensor_delay_sec)
|
|
256
|
+
self._sensorstatus = next_status
|
|
257
|
+
LOGGER.debug("Zone%s (%s) - sensorstatus: %s", self._zone_id, self.sensorname, next_status)
|
|
258
|
+
self.notify()
|
|
259
|
+
|
|
232
260
|
@battery_status.setter
|
|
233
261
|
def battery_status(self, value: str) -> None:
|
|
234
262
|
if self._battery_status != value:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_automation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_doorlock.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_eu_event.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_heat_map.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_history.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_partition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_scene.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_sensor.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_shades.py
RENAMED
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_state.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_tcc.py
RENAMED
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_thermostat.py
RENAMED
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_user.py
RENAMED
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_weather.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/database/table_zwave_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qolsys_controller-0.0.34 → qolsys_controller-0.0.35}/qolsys_controller/mqtt_command_queue.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|