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,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.plugin = None
19
- self._state = QolsysState()
20
- self._settings = QolsysSettings()
21
- self._panel = QolsysPanel(settings=self.settings, state=self.state)
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.plugin = QolsysPluginC4(self.state, self.panel, self.settings)
52
+ self._plugin = QolsysPluginC4(self)
42
53
  return
43
54
 
44
55
  case "remote":
45
56
  LOGGER.debug("Remote Plugin Selected")
46
- self.plugin = QolsysPluginRemote(self.state, self.panel, self.settings)
57
+ self._plugin = QolsysPluginRemote(self)
47
58
  return
48
59
 
49
60
  case _:
50
- LOGGER.debug("Unknow Plugin Selected")
61
+ LOGGER.error("Unknow Plugin Selected")
@@ -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
- # New Values to update in table
116
- db_value = ",".join([f"{key}='{value}'" for key, value in content_value.items()])
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
@@ -17,6 +17,7 @@ class QolsysTableDoorLock(QolsysTable):
17
17
 
18
18
  self._columns = [
19
19
  "_id",
20
+ "capabilities",
20
21
  "version",
21
22
  "opr",
22
23
  "partition_id",
@@ -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 = False
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 = False
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
  ]
@@ -39,6 +39,7 @@ class QolsysTableUser(QolsysTable):
39
39
  "start_date",
40
40
  "tag_flag",
41
41
  "check_in_time",
42
+ "user_feature1",
42
43
  ]
43
44
 
44
45
  self._create_table()
@@ -57,6 +57,7 @@ class QolsysTableZwaveNode(QolsysTable):
57
57
  "last_rediscover_time",
58
58
  "neighbour_info",
59
59
  "last_node_test_time",
60
+ "notification_capabilities",
60
61
  "endpoint",
61
62
  "endpoint_details",
62
63
  "device_wakeup_time",
@@ -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 = False
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
@@ -34,4 +34,3 @@ class ThermostatFanMode(Enum):
34
34
  QUIET: 0x0400
35
35
  EXTERNAL_CIRCULATION: 0x0800
36
36
  MANUFACTURER_SPECEFIC: 0x1000
37
-
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
- self.azc = AsyncZeroconf()
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.",