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/controller.py
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import logging
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from qolsys_controller.plugin import QolsysPlugin
|
|
3
8
|
|
|
4
9
|
from .panel import QolsysPanel
|
|
5
10
|
from .plugin_c4 import QolsysPluginC4
|
|
@@ -9,21 +14,27 @@ from .state import QolsysState
|
|
|
9
14
|
|
|
10
15
|
LOGGER = logging.getLogger(__name__)
|
|
11
16
|
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .plugin import QolsysPlugin
|
|
12
19
|
|
|
13
20
|
class QolsysController:
|
|
14
21
|
|
|
15
22
|
def __init__(self) -> None:
|
|
16
23
|
|
|
17
24
|
# QolsysController Information
|
|
18
|
-
self.
|
|
19
|
-
self._state = QolsysState()
|
|
20
|
-
self._settings = QolsysSettings()
|
|
21
|
-
self._panel = QolsysPanel(
|
|
25
|
+
self._plugin: QolsysPlugin | None = None
|
|
26
|
+
self._state = QolsysState(self)
|
|
27
|
+
self._settings = QolsysSettings(self)
|
|
28
|
+
self._panel = QolsysPanel(self)
|
|
22
29
|
|
|
23
30
|
@property
|
|
24
31
|
def state(self) -> QolsysState:
|
|
25
32
|
return self._state
|
|
26
33
|
|
|
34
|
+
@property
|
|
35
|
+
def plugin(self) -> QolsysPlugin:
|
|
36
|
+
return self._plugin
|
|
37
|
+
|
|
27
38
|
@property
|
|
28
39
|
def panel(self) -> QolsysPanel:
|
|
29
40
|
return self._panel
|
|
@@ -38,13 +49,13 @@ class QolsysController:
|
|
|
38
49
|
|
|
39
50
|
case "c4":
|
|
40
51
|
LOGGER.debug("C4 Plugin Selected")
|
|
41
|
-
self.
|
|
52
|
+
self._plugin = QolsysPluginC4(self)
|
|
42
53
|
return
|
|
43
54
|
|
|
44
55
|
case "remote":
|
|
45
56
|
LOGGER.debug("Remote Plugin Selected")
|
|
46
|
-
self.
|
|
57
|
+
self._plugin = QolsysPluginRemote(self)
|
|
47
58
|
return
|
|
48
59
|
|
|
49
60
|
case _:
|
|
50
|
-
LOGGER.
|
|
61
|
+
LOGGER.error("Unknow Plugin Selected")
|
qolsys_controller/database/db.py
CHANGED
|
@@ -237,6 +237,37 @@ class QolsysDB:
|
|
|
237
237
|
|
|
238
238
|
return zones
|
|
239
239
|
|
|
240
|
+
def get_weather(self) -> list[dict]:
|
|
241
|
+
self.cursor.execute(f"SELECT * FROM {self.table_weather.table} ORDER BY _id")
|
|
242
|
+
self.db.commit()
|
|
243
|
+
|
|
244
|
+
weather_list = []
|
|
245
|
+
columns = [description[0] for description in self.cursor.description]
|
|
246
|
+
for row in self.cursor.fetchall():
|
|
247
|
+
row_dict = dict(zip(columns, row, strict=True))
|
|
248
|
+
weather_list.append(row_dict)
|
|
249
|
+
|
|
250
|
+
return weather_list
|
|
251
|
+
|
|
252
|
+
def get_powerg(self, short_id: str) -> dict:
|
|
253
|
+
try:
|
|
254
|
+
self.cursor.execute(f"SELECT * FROM {self.table_powerg_device.table} WHERE shortID = ?",(short_id,))
|
|
255
|
+
self.db.commit()
|
|
256
|
+
|
|
257
|
+
row = self.cursor.fetchone()
|
|
258
|
+
|
|
259
|
+
if row is None:
|
|
260
|
+
LOGGER.debug("%s value not found", short_id)
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
columns = [description[0] for description in self.cursor.description]
|
|
264
|
+
return dict(zip(columns, row, strict=True))
|
|
265
|
+
|
|
266
|
+
except sqlite3.Error:
|
|
267
|
+
LOGGER.exception("Error getting PowerG device info for shortID %s", short_id)
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
|
|
240
271
|
def get_setting_panel(self, setting: str) -> str:
|
|
241
272
|
self.cursor.execute(f"""SELECT value FROM {self.table_qolsyssettings.table}
|
|
242
273
|
WHERE name = ? and partition_id = ? """, (setting, "0"))
|
|
@@ -68,6 +68,7 @@ class QolsysTable:
|
|
|
68
68
|
raise error from err
|
|
69
69
|
|
|
70
70
|
def insert(self, data: dict) -> None:
|
|
71
|
+
|
|
71
72
|
try:
|
|
72
73
|
if not self._implemented and data is not None:
|
|
73
74
|
LOGGER.warning("New Table format: %s", self.uri)
|
|
@@ -92,6 +93,7 @@ class QolsysTable:
|
|
|
92
93
|
|
|
93
94
|
col_str = ", ".join(full_data.keys())
|
|
94
95
|
placeholder_str = ", ".join([f":{key}" for key in full_data])
|
|
96
|
+
|
|
95
97
|
query = f"INSERT OR IGNORE INTO {self.table} ({col_str}) VALUES ({placeholder_str})"
|
|
96
98
|
self._cursor.execute(query, full_data)
|
|
97
99
|
self._db.commit()
|
|
@@ -108,12 +110,18 @@ class QolsysTable:
|
|
|
108
110
|
|
|
109
111
|
def update(self, selection: str, selection_argument: str, content_value: str) -> None:
|
|
110
112
|
# selection: 'zone_id=?, parition_id=?'
|
|
113
|
+
|
|
114
|
+
# selection_argument:
|
|
111
115
|
# Firmware 4.4.1: selection_argument: '[3,1]'
|
|
112
116
|
# Firmware 4.6.1: selection_argument: ['3','1']
|
|
113
117
|
# contentValues:{"partition_id":"0","sensorgroup":"safetymotion","sensorstatus":"Idle"}"
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
if selection_argument == "":
|
|
120
|
+
LOGGER.debug("Update called with empty selection_argument")
|
|
121
|
+
LOGGER.debug("Table: %s", self.table)
|
|
122
|
+
LOGGER.debug("Selection: %s", selection)
|
|
123
|
+
LOGGER.debug("selection_argument: %s", selection_argument)
|
|
124
|
+
return
|
|
117
125
|
|
|
118
126
|
# Selection Argument
|
|
119
127
|
# Panel send selection_argument as list in Firmware 4.6.1
|
|
@@ -123,6 +131,24 @@ class QolsysTable:
|
|
|
123
131
|
selection_argument = [item.strip() for item in selection_argument.split(",")]
|
|
124
132
|
|
|
125
133
|
try:
|
|
134
|
+
full_data = {}
|
|
135
|
+
new_columns = []
|
|
136
|
+
|
|
137
|
+
for key, value in content_value.items():
|
|
138
|
+
if key in self._columns:
|
|
139
|
+
full_data[key] = value
|
|
140
|
+
else:
|
|
141
|
+
new_columns.append(key)
|
|
142
|
+
|
|
143
|
+
db_value = ",".join([f"{key}='{value}'" for key, value in full_data.items()])
|
|
144
|
+
|
|
145
|
+
# Warn if new column found in iq2meid database
|
|
146
|
+
if new_columns:
|
|
147
|
+
LOGGER.warning("New column found in iq2meid database")
|
|
148
|
+
LOGGER.warning("Table: %s", self.table)
|
|
149
|
+
LOGGER.warning("New Columns: %s", new_columns)
|
|
150
|
+
LOGGER.warning("Please Report")
|
|
151
|
+
|
|
126
152
|
query = f"UPDATE {self.table} SET {db_value} WHERE {selection}"
|
|
127
153
|
self._cursor.execute(query, selection_argument)
|
|
128
154
|
self._db.commit()
|
|
@@ -145,6 +171,14 @@ class QolsysTable:
|
|
|
145
171
|
# Firmware 4.4.1: selection_argument: '[3,1]'
|
|
146
172
|
# Firmware 4.6.1: selection_argument: ['3','1']
|
|
147
173
|
|
|
174
|
+
|
|
175
|
+
if selection_argument == "":
|
|
176
|
+
LOGGER.debug("Delete called with empty selection_argument")
|
|
177
|
+
LOGGER.debug("Table: %s", self.table)
|
|
178
|
+
LOGGER.debug("Selection: %s", selection)
|
|
179
|
+
LOGGER.debug("selection_argument: %s", selection_argument)
|
|
180
|
+
return
|
|
181
|
+
|
|
148
182
|
# Selection Argument
|
|
149
183
|
if(type(selection_argument) is not list):
|
|
150
184
|
#Firmware 4.4.1, seletion_argument is sent as a string
|
|
@@ -13,12 +13,21 @@ class QolsysTableEuEvent(QolsysTable):
|
|
|
13
13
|
self._uri = "content://com.qolsys.qolsysprovider.EUEventContentProvider/eu_event"
|
|
14
14
|
self._table = "eu_event"
|
|
15
15
|
self._abort_on_error = False
|
|
16
|
-
self._implemented =
|
|
16
|
+
self._implemented = True
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
self._columns = [
|
|
20
20
|
"_id",
|
|
21
|
+
"version",
|
|
22
|
+
"opr",
|
|
23
|
+
"partition_id",
|
|
24
|
+
"history_id",
|
|
25
|
+
"device_id",
|
|
26
|
+
"device",
|
|
27
|
+
"events",
|
|
28
|
+
"time",
|
|
29
|
+
"ack",
|
|
30
|
+
"type",
|
|
21
31
|
]
|
|
22
32
|
|
|
23
33
|
self._create_table()
|
|
24
|
-
|
|
@@ -13,11 +13,35 @@ class QolsysTablePowerGDevice(QolsysTable):
|
|
|
13
13
|
self._uri = "content://com.qolsys.qolsysprovider.PowerGDeviceContentProvider/powerg_device"
|
|
14
14
|
self._table = "powerg_device"
|
|
15
15
|
self._abort_on_error = False
|
|
16
|
-
self._implemented =
|
|
16
|
+
self._implemented = True
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
self._columns = [
|
|
20
20
|
"_id",
|
|
21
|
+
"avg_link_quality",
|
|
22
|
+
"battery_voltage",
|
|
23
|
+
"capabilities",
|
|
24
|
+
"dealer_code",
|
|
25
|
+
"extras",
|
|
26
|
+
"firmware_version",
|
|
27
|
+
"led",
|
|
28
|
+
"light",
|
|
29
|
+
"link_quality",
|
|
30
|
+
"link_status",
|
|
31
|
+
"longID",
|
|
32
|
+
"manufacturing_id",
|
|
33
|
+
"notification_period",
|
|
34
|
+
"opr",
|
|
35
|
+
"parent_node",
|
|
36
|
+
"partition_id",
|
|
37
|
+
"radio_id",
|
|
38
|
+
"radio_version",
|
|
39
|
+
"shortID",
|
|
40
|
+
"status_data",
|
|
41
|
+
"supported_type",
|
|
42
|
+
"temperature",
|
|
43
|
+
"version",
|
|
44
|
+
"writeable_capabilities",
|
|
21
45
|
]
|
|
22
46
|
|
|
23
47
|
self._create_table()
|
|
@@ -42,6 +42,7 @@ class QolsysTableSensor(QolsysTable):
|
|
|
42
42
|
"zone_reporting_enabled",
|
|
43
43
|
"zone_feature1",
|
|
44
44
|
"zone_feature2",
|
|
45
|
+
"zone_feature3",
|
|
45
46
|
"battery_status",
|
|
46
47
|
"created_date",
|
|
47
48
|
"created_by",
|
|
@@ -61,6 +62,7 @@ class QolsysTableSensor(QolsysTable):
|
|
|
61
62
|
"latestdBm",
|
|
62
63
|
"averagedBm",
|
|
63
64
|
"serial_number",
|
|
65
|
+
"secondary_panel_mac_address",
|
|
64
66
|
"extras",
|
|
65
67
|
"allowspeaker",
|
|
66
68
|
]
|
|
@@ -13,10 +13,23 @@ class QolsysTableZwaveOther(QolsysTable):
|
|
|
13
13
|
self._uri = "content://com.qolsys.qolsysprovider.ZwaveOtherDeviceContentProvider/zwave_other"
|
|
14
14
|
self._table = "zwave_other"
|
|
15
15
|
self._abort_on_error = False
|
|
16
|
-
self._implemented =
|
|
16
|
+
self._implemented = True
|
|
17
17
|
|
|
18
18
|
self._columns = [
|
|
19
19
|
"_id",
|
|
20
|
+
"created_date",
|
|
21
|
+
"device_name",
|
|
22
|
+
"device_params_1",
|
|
23
|
+
"device_params_2",
|
|
24
|
+
"endpoint",
|
|
25
|
+
"last_updated_date",
|
|
26
|
+
"node_id",
|
|
27
|
+
"node_type",
|
|
28
|
+
"opr",
|
|
29
|
+
"paired_status",
|
|
30
|
+
"partition_id",
|
|
31
|
+
"status",
|
|
32
|
+
"version",
|
|
20
33
|
]
|
|
21
34
|
|
|
22
35
|
self._create_table()
|
qolsys_controller/enum.py
CHANGED
|
@@ -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"
|
|
@@ -46,6 +47,7 @@ class ZoneStatus(StrEnum):
|
|
|
46
47
|
|
|
47
48
|
class ZoneSensorType(StrEnum):
|
|
48
49
|
DOOR_WINDOW = "Door_Window"
|
|
50
|
+
DOORBELL = "Doorbell"
|
|
49
51
|
MOTION = "Motion"
|
|
50
52
|
GLASS_BREAK = "GlassBreak"
|
|
51
53
|
KEY_FOB = "KeyFob"
|
|
@@ -61,7 +63,6 @@ class ZoneSensorType(StrEnum):
|
|
|
61
63
|
SHOCK = "Shock"
|
|
62
64
|
FREEZE = "Freeze"
|
|
63
65
|
TILT = "Tilt"
|
|
64
|
-
DOORBELL = "Doorbell"
|
|
65
66
|
SMOKE_M = "Smoke_M"
|
|
66
67
|
# DOOR_WINDOW_M = "" #TBD
|
|
67
68
|
# OCCUPANCY = "" #TBD
|
|
@@ -72,35 +73,38 @@ class ZoneSensorType(StrEnum):
|
|
|
72
73
|
BLUETOOTH = "Bluetooth"
|
|
73
74
|
TAKEOVER_MODULE = "TakeoverModule"
|
|
74
75
|
TRANSLATOR = "Translator"
|
|
76
|
+
TAMPER = "Tamper Sensor"
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
class ZoneSensorGroup(StrEnum):
|
|
80
|
+
CO = "co"
|
|
78
81
|
FIXED_INTRUSION = "fixedintrusion"
|
|
79
|
-
MOBILE_INTRUSION = "mobileintrusion"
|
|
80
82
|
FIXED_SILENT = "fixedsilentkey"
|
|
83
|
+
MOBILE_INTRUSION = "mobileintrusion"
|
|
81
84
|
MOBILE_SILENT = "mobilesilentkey"
|
|
82
85
|
FIXED_AUXILIARY = "fixedmedical"
|
|
83
86
|
FIXED_SILENT_AUXILIARY = "fixedsilentmedical"
|
|
87
|
+
LOCAL_SAFETY_SENSOR = "localsafety"
|
|
84
88
|
MOBILE_AUXILIARY = "mobilemedical"
|
|
85
89
|
MOBILE_SILENT_AUXILIARY = "mobilesilentmedical"
|
|
86
90
|
SAFETY_MOTION = "safetymotion"
|
|
87
91
|
GLASS_BREAK = "glassbreak"
|
|
88
92
|
GLASS_BREAK_AWAY_ONLY = "glassbreakawayonly"
|
|
93
|
+
SMOKE_HEAT = "smoke_heat"
|
|
94
|
+
TAMPER_ZONE = "tamperzone"
|
|
95
|
+
SHOCK = "shock"
|
|
89
96
|
|
|
90
97
|
# ENTRY_EXIT_NORMAL_DELAY = "" #TBD
|
|
91
98
|
# ENTRY_EXIT_LONG_DELAY = "" #TBD
|
|
92
99
|
# INSTANT_PERIMETER_DW = "" #TBD
|
|
93
100
|
# INSTRANT_INTERIOR_DOOR = "" #TBD
|
|
94
101
|
# AWAY_INSTANT_FOLLOWER_DELAY = "" #TBD
|
|
95
|
-
# LOCAL_SAFETY_SENSOR = "" #TBD
|
|
96
102
|
# REPORTING_SAFETY_SENSOR = "" #TBD
|
|
97
103
|
# DELAYED_REPORTING_SAFETY_SENSOR = "" #TBD
|
|
98
104
|
# AWAY_INSTANT_MOTION = "" #TBD
|
|
99
105
|
# STAY_INSTANT_MOTION = "" #TBD
|
|
100
106
|
# STAY_DELAY_MOTION = "" #TBD
|
|
101
107
|
# AWAY_DELAY_MOTION = "" #TBD
|
|
102
|
-
# SMOKE_HEAT = "" # TBD
|
|
103
|
-
# CO = "" # TBD
|
|
104
108
|
# TAKEOVER = "" #TBD
|
|
105
109
|
# GARAGE_TILT_SAFETY = "" # TBD
|
|
106
110
|
# WATER_SENSOR = "" # TBD
|
qolsys_controller/enum_zwave.py
CHANGED
qolsys_controller/mdns.py
CHANGED
|
@@ -6,9 +6,14 @@ from zeroconf.asyncio import AsyncZeroconf
|
|
|
6
6
|
|
|
7
7
|
class QolsysMDNS:
|
|
8
8
|
|
|
9
|
-
def __init__(self, ip: str, port: int) -> None:
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
def __init__(self, ip: str, port: int, external_zero_conf: AsyncZeroconf | None = None) -> None:
|
|
10
|
+
|
|
11
|
+
# Add possible external zeroconf instance provided by Home Assistant by example
|
|
12
|
+
# If no external instance is provided, create our own
|
|
13
|
+
if external_zero_conf:
|
|
14
|
+
self.azc = external_zero_conf
|
|
15
|
+
else:
|
|
16
|
+
self.azc = AsyncZeroconf()
|
|
12
17
|
|
|
13
18
|
self.mdns_info = ServiceInfo(
|
|
14
19
|
"_http._tcp.local.",
|