qolsys-controller 0.0.38__py3-none-any.whl → 0.0.62__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.

Files changed (73) hide show
  1. qolsys_controller/controller.py +829 -20
  2. qolsys_controller/database/db.py +63 -25
  3. qolsys_controller/database/table.py +89 -60
  4. qolsys_controller/database/table_alarmedsensor.py +0 -2
  5. qolsys_controller/database/table_automation.py +0 -1
  6. qolsys_controller/database/table_country_locale.py +0 -1
  7. qolsys_controller/database/table_dashboard_msgs.py +1 -2
  8. qolsys_controller/database/table_dimmerlight.py +0 -1
  9. qolsys_controller/database/table_doorlock.py +1 -1
  10. qolsys_controller/database/table_eu_event.py +12 -4
  11. qolsys_controller/database/table_heat_map.py +0 -2
  12. qolsys_controller/database/table_history.py +4 -1
  13. qolsys_controller/database/table_iqremotesettings.py +0 -2
  14. qolsys_controller/database/table_iqrouter_network_config.py +0 -1
  15. qolsys_controller/database/table_iqrouter_user_device.py +0 -2
  16. qolsys_controller/database/table_master_slave.py +0 -1
  17. qolsys_controller/database/table_nest_device.py +0 -1
  18. qolsys_controller/database/table_output_rules.py +0 -1
  19. qolsys_controller/database/table_partition.py +0 -1
  20. qolsys_controller/database/table_pgm_outputs.py +0 -2
  21. qolsys_controller/database/table_powerg_device.py +25 -3
  22. qolsys_controller/database/table_qolsyssettings.py +0 -2
  23. qolsys_controller/database/table_scene.py +0 -2
  24. qolsys_controller/database/table_sensor.py +3 -2
  25. qolsys_controller/database/table_sensor_group.py +23 -0
  26. qolsys_controller/database/table_shades.py +0 -2
  27. qolsys_controller/database/table_smartsocket.py +0 -2
  28. qolsys_controller/database/table_state.py +0 -1
  29. qolsys_controller/database/table_tcc.py +0 -1
  30. qolsys_controller/database/table_thermostat.py +0 -1
  31. qolsys_controller/database/table_trouble_conditions.py +0 -2
  32. qolsys_controller/database/table_user.py +1 -2
  33. qolsys_controller/database/table_virtual_device.py +0 -2
  34. qolsys_controller/database/table_weather.py +0 -2
  35. qolsys_controller/database/table_zigbee_device.py +0 -1
  36. qolsys_controller/database/table_zwave_association_group.py +0 -1
  37. qolsys_controller/database/table_zwave_history.py +0 -1
  38. qolsys_controller/database/table_zwave_node.py +1 -1
  39. qolsys_controller/database/table_zwave_other.py +14 -2
  40. qolsys_controller/enum.py +44 -16
  41. qolsys_controller/enum_zwave.py +81 -36
  42. qolsys_controller/errors.py +9 -12
  43. qolsys_controller/mdns.py +7 -4
  44. qolsys_controller/mqtt_command.py +119 -0
  45. qolsys_controller/mqtt_command_queue.py +5 -4
  46. qolsys_controller/observable.py +2 -2
  47. qolsys_controller/panel.py +224 -143
  48. qolsys_controller/partition.py +129 -127
  49. qolsys_controller/pki.py +69 -97
  50. qolsys_controller/scene.py +30 -28
  51. qolsys_controller/settings.py +96 -50
  52. qolsys_controller/state.py +71 -33
  53. qolsys_controller/task_manager.py +8 -12
  54. qolsys_controller/users.py +25 -0
  55. qolsys_controller/utils_mqtt.py +8 -16
  56. qolsys_controller/weather.py +71 -0
  57. qolsys_controller/zone.py +307 -169
  58. qolsys_controller/zwave_device.py +108 -95
  59. qolsys_controller/zwave_dimmer.py +53 -50
  60. qolsys_controller/zwave_garagedoor.py +0 -1
  61. qolsys_controller/zwave_generic.py +2 -3
  62. qolsys_controller/zwave_lock.py +47 -44
  63. qolsys_controller/zwave_outlet.py +0 -1
  64. qolsys_controller/zwave_thermostat.py +112 -118
  65. qolsys_controller-0.0.62.dist-info/METADATA +89 -0
  66. qolsys_controller-0.0.62.dist-info/RECORD +69 -0
  67. {qolsys_controller-0.0.38.dist-info → qolsys_controller-0.0.62.dist-info}/WHEEL +1 -1
  68. qolsys_controller/plugin.py +0 -34
  69. qolsys_controller/plugin_c4.py +0 -17
  70. qolsys_controller/plugin_remote.py +0 -1298
  71. qolsys_controller-0.0.38.dist-info/METADATA +0 -93
  72. qolsys_controller-0.0.38.dist-info/RECORD +0 -68
  73. {qolsys_controller-0.0.38.dist-info → qolsys_controller-0.0.62.dist-info}/licenses/LICENSE +0 -0
@@ -7,42 +7,44 @@ LOGGER = logging.getLogger(__name__)
7
7
 
8
8
 
9
9
  class QolsysThermostat(QolsysZWaveDevice):
10
-
11
- def __init__(self, thermostat_dict: dict, zwave_dict: dict) -> None:
12
-
10
+ def __init__(self, thermostat_dict: dict[str, str], zwave_dict: dict[str, str]) -> None:
13
11
  super().__init__(zwave_dict)
14
12
 
15
- self._thermostat_id = thermostat_dict.get("_id")
16
- self._thermostat_version = thermostat_dict.get("version", "")
17
- self._thermostat_opr = thermostat_dict.get("opr", "")
18
- self._thermostat_partition_id = thermostat_dict.get("partition_id", "")
19
- self._thermostat_name = thermostat_dict.get("thermostat_name", "")
20
- self._thermostat_node_id = thermostat_dict.get("node_id", "")
21
- self._thermostat_device_temp_unit = thermostat_dict.get("device_temp_unit", "") # 'F'
22
- self._thermostat_current_temp = thermostat_dict.get("current_temp", "") # "78.0"
23
- self._thermostat_target_cool_temp = thermostat_dict.get("target_cool_temp", "") # "78"
24
- self._thermostat_target_heat_temp = thermostat_dict.get("target_heat_temp", "") # "65"
25
- self._thermostat_target_temp = thermostat_dict.get("target_temp", "")
26
- self._thermostat_power_usage = thermostat_dict.get("power_usage", "")
27
- self._thermostat_mode = thermostat_dict.get("thermostat_mode", "") # "2"
28
- self._thermostat_mode_bitmask = thermostat_dict.get("thermostat_mode_bitmask", "") # "7,24"
29
- self._thermostat_fan_mode = thermostat_dict.get("fan_mode", "") # "0"
30
- self._thermostat_fan_mode_bitmask = thermostat_dict.get("fan_mode_bitmask", "") # "67"
31
- self._thermostat_set_point_mode = thermostat_dict.get("set_point_mode", "")
32
- self._thermostat_set_point_mode_bitmask = thermostat_dict.get("set_point_mode_bitmask", "") # "-122,1"
33
- self._thermostat_created_by = thermostat_dict.get("created_by", "")
34
- self._thermostat_created_date = thermostat_dict.get("created_date", "")
35
- self._thermostat_updated_by = thermostat_dict.get("updated_by", "")
36
- self._thermostat_last_updated_date = thermostat_dict.get("last_updated_date", "")
37
- self._thermostat_mode_updated_time = thermostat_dict.get("thermostat_mode_updated_time", "")
38
- self._thermostat_fan_mode_updated_time = thermostat_dict.get("fan_mode_updated_time", "")
39
- self._thermostat_set_point_mode_updated_time = thermostat_dict.get("set_point_mode_updated_time", "")
40
- self._thermostat_target_cool_temp_updated_time = thermostat_dict.get("target_cool_temp_updated_time", "")
41
- self._thermostat_target_heat_temp_updated_time = thermostat_dict.get("target_heat_temp_updated_time", "")
42
- self._thermostat_current_temp_updated_time = thermostat_dict.get("current_temp_updated_time", "")
43
- self._thermostat_endpoint = thermostat_dict.get("endpoint", "")
44
- self._thermostat_paired_status = thermostat_dict.get("paired_status", "")
45
- self._thermostat_configuration_parameter = thermostat_dict.get("configuration_parameter", "")
13
+ self._thermostat_id: str = thermostat_dict.get("_id", "")
14
+ self._thermostat_version: str = thermostat_dict.get("version", "")
15
+ self._thermostat_opr: str = thermostat_dict.get("opr", "")
16
+ self._thermostat_partition_id: str = thermostat_dict.get("partition_id", "")
17
+ self._thermostat_name: str = thermostat_dict.get("thermostat_name", "")
18
+ self._thermostat_node_id: str = thermostat_dict.get("node_id", "")
19
+ self._thermostat_device_temp_unit: str = thermostat_dict.get("device_temp_unit", "") # 'F'
20
+ self._thermostat_current_temp: str = thermostat_dict.get("current_temp", "") # "78.0"
21
+ self._thermostat_target_cool_temp: str = thermostat_dict.get("target_cool_temp", "") # "78"
22
+ self._thermostat_target_heat_temp: str = thermostat_dict.get("target_heat_temp", "") # "65"
23
+ self._thermostat_target_temp: str = thermostat_dict.get("target_temp", "")
24
+ self._thermostat_power_usage: str = thermostat_dict.get("power_usage", "")
25
+ self._thermostat_mode: str = thermostat_dict.get("thermostat_mode", "") # "2"
26
+ self._thermostat_mode_bitmask: str = thermostat_dict.get("thermostat_mode_bitmask", "") # "7,24"
27
+ self._thermostat_fan_mode: str = thermostat_dict.get("fan_mode", "") # "0"
28
+ self._thermostat_fan_mode_bitmask: str = thermostat_dict.get("fan_mode_bitmask", "") # "67"
29
+ self._thermostat_set_point_mode: str = thermostat_dict.get("set_point_mode", "")
30
+ self._thermostat_set_point_mode_bitmask: str = thermostat_dict.get("set_point_mode_bitmask", "") # "-122,1"
31
+ self._thermostat_created_by: str = thermostat_dict.get("created_by", "")
32
+ self._thermostat_created_date: str = thermostat_dict.get("created_date", "")
33
+ self._thermostat_updated_by: str = thermostat_dict.get("updated_by", "")
34
+ self._thermostat_last_updated_date: str = thermostat_dict.get("last_updated_date", "")
35
+ self._thermostat_mode_updated_time: str = thermostat_dict.get("thermostat_mode_updated_time", "")
36
+ self._thermostat_fan_mode_updated_time: str = thermostat_dict.get("fan_mode_updated_time", "")
37
+ self._thermostat_set_point_mode_updated_time: str = thermostat_dict.get("set_point_mode_updated_time", "")
38
+ self._thermostat_target_cool_temp_updated_time: str = thermostat_dict.get("target_cool_temp_updated_time", "")
39
+ self._thermostat_target_heat_temp_updated_time: str = thermostat_dict.get("target_heat_temp_updated_time", "")
40
+ self._thermostat_current_temp_updated_time: str = thermostat_dict.get("current_temp_updated_time", "")
41
+ self._thermostat_endpoint: str = thermostat_dict.get("endpoint", "")
42
+ self._thermostat_paired_status: str = thermostat_dict.get("paired_status", "")
43
+ self._thermostat_configuration_parameter: str = thermostat_dict.get("configuration_parameter", "")
44
+
45
+ # -----------------------------
46
+ # properties + setters
47
+ # -----------------------------
46
48
 
47
49
  @property
48
50
  def thermostat_node_id(self) -> str:
@@ -52,38 +54,6 @@ class QolsysThermostat(QolsysZWaveDevice):
52
54
  def thermostat_name(self) -> str:
53
55
  return self._thermostat_name
54
56
 
55
- @property
56
- def thermostat_device_temp_unit(self) -> str:
57
- return self._thermostat_device_temp_unit
58
-
59
- @property
60
- def thermostat_current_temp(self) -> str:
61
- return self._thermostat_current_temp
62
-
63
- @property
64
- def thermostat_target_cool_temp(self) -> str:
65
- return self._thermostat_target_cool_temp
66
-
67
- @property
68
- def thermostat_target_heat_temp(self) -> str:
69
- return self._thermostat_target_heat_temp
70
-
71
- @property
72
- def thermostat_target_temp(self) -> str:
73
- return self._thermostat_target_temp
74
-
75
- @property
76
- def thermostat_mode(self) -> str:
77
- return self._thermostat_mode
78
-
79
- @property
80
- def thermostat_fan_mode(self) -> str:
81
- return self.thermostat_fan_mode
82
-
83
- @property
84
- def thermostat_set_point_mode(self) -> str:
85
- return self._thermostat_set_point_mode
86
-
87
57
  @thermostat_name.setter
88
58
  def thermostat_name(self, value: str) -> None:
89
59
  if self._thermostat_name != value:
@@ -91,6 +61,10 @@ class QolsysThermostat(QolsysZWaveDevice):
91
61
  self._thermostat_name = value
92
62
  self.notify()
93
63
 
64
+ @property
65
+ def thermostat_device_temp_unit(self) -> str:
66
+ return self._thermostat_device_temp_unit
67
+
94
68
  @thermostat_device_temp_unit.setter
95
69
  def thermostat_device_temp_unit(self, value: str) -> None:
96
70
  if self._thermostat_device_temp_unit != value:
@@ -98,6 +72,10 @@ class QolsysThermostat(QolsysZWaveDevice):
98
72
  self._thermostat_device_temp_unit = value
99
73
  self.notify()
100
74
 
75
+ @property
76
+ def thermostat_current_temp(self) -> str:
77
+ return self._thermostat_current_temp
78
+
101
79
  @thermostat_current_temp.setter
102
80
  def thermostat_current_temp(self, value: str) -> None:
103
81
  if self._thermostat_current_temp != value:
@@ -105,6 +83,10 @@ class QolsysThermostat(QolsysZWaveDevice):
105
83
  self._thermostat_current_temp = value
106
84
  self.notify()
107
85
 
86
+ @property
87
+ def thermostat_target_cool_temp(self) -> str:
88
+ return self._thermostat_target_cool_temp
89
+
108
90
  @thermostat_target_cool_temp.setter
109
91
  def thermostat_target_cool_temp(self, value: str) -> None:
110
92
  if self._thermostat_target_cool_temp != value:
@@ -112,6 +94,10 @@ class QolsysThermostat(QolsysZWaveDevice):
112
94
  self._thermostat_target_cool_temp = value
113
95
  self.notify()
114
96
 
97
+ @property
98
+ def thermostat_target_heat_temp(self) -> str:
99
+ return self._thermostat_target_heat_temp
100
+
115
101
  @thermostat_target_heat_temp.setter
116
102
  def thermostat_target_heat_temp(self, value: str) -> None:
117
103
  if self._thermostat_target_heat_temp != value:
@@ -119,6 +105,10 @@ class QolsysThermostat(QolsysZWaveDevice):
119
105
  self._thermostat_target_heat_temp = value
120
106
  self.notify()
121
107
 
108
+ @property
109
+ def thermostat_target_temp(self) -> str:
110
+ return self._thermostat_target_temp
111
+
122
112
  @thermostat_target_temp.setter
123
113
  def thermostat_target_temp(self, value: str) -> None:
124
114
  if self._thermostat_target_temp != value:
@@ -126,6 +116,14 @@ class QolsysThermostat(QolsysZWaveDevice):
126
116
  self._thermostat_target_temp = value
127
117
  self.notify()
128
118
 
119
+ @property
120
+ def thermostat_mode(self) -> ThermostatMode | None:
121
+ thermostat_mode = int(self._thermostat_mode)
122
+ for mode in ThermostatMode:
123
+ if thermostat_mode == mode:
124
+ return mode
125
+ return None
126
+
129
127
  @thermostat_mode.setter
130
128
  def thermostat_mode(self, value: str) -> None:
131
129
  if self._thermostat_mode != value:
@@ -133,6 +131,14 @@ class QolsysThermostat(QolsysZWaveDevice):
133
131
  self._thermostat_mode = value
134
132
  self.notify()
135
133
 
134
+ @property
135
+ def thermostat_fan_mode(self) -> ThermostatFanMode | None:
136
+ thermostat_fan_mode = int(self._thermostat_fan_mode)
137
+ for mode in ThermostatFanMode:
138
+ if thermostat_fan_mode == mode:
139
+ return mode
140
+ return None
141
+
136
142
  @thermostat_fan_mode.setter
137
143
  def thermostat_fan_mode(self, value: str) -> None:
138
144
  if self._thermostat_fan_mode != value:
@@ -140,6 +146,10 @@ class QolsysThermostat(QolsysZWaveDevice):
140
146
  self._thermostat_fan_mode = value
141
147
  self.notify()
142
148
 
149
+ @property
150
+ def thermostat_set_point_mode(self) -> str:
151
+ return self._thermostat_set_point_mode
152
+
143
153
  @thermostat_set_point_mode.setter
144
154
  def thermostat_set_point_mode(self, value: str) -> None:
145
155
  if self._thermostat_set_point_mode != value:
@@ -147,78 +157,80 @@ class QolsysThermostat(QolsysZWaveDevice):
147
157
  self._thermostat_set_point_mode = value
148
158
  self.notify()
149
159
 
150
- def update_thermostat(self, data: dict) -> None: # noqa: C901, PLR0912, PLR0915
160
+ def update_thermostat(self, data: dict[str, str]) -> None: # noqa: C901, PLR0912, PLR0915
151
161
  # Check if we are updating same none_id
152
162
  node_id_update = data.get("node_id", "")
153
- if node_id_update != self.thermostat_node_id_node_id:
163
+ if node_id_update != self.thermostat_node_id:
154
164
  LOGGER.error(
155
165
  "Updating Thermostat '%s' (%s) with Thermostat '%s' (different id)",
156
- self.thermostat_node_id, self.thermostat_name, node_id_update,
166
+ self.thermostat_node_id,
167
+ self.thermostat_name,
168
+ node_id_update,
157
169
  )
158
170
  return
159
171
 
160
172
  self.start_batch_update()
161
173
 
162
174
  if "version" in data:
163
- self._thermostat_version = data.get("version")
175
+ self._thermostat_version = data.get("version", "")
164
176
  if "opr" in data:
165
- self._thermostat_opr = data.get("opr")
177
+ self._thermostat_opr = data.get("opr", "")
166
178
  if "partition_id" in data:
167
- self._thermostat_partition_id = data.get("partition_id")
179
+ self._thermostat_partition_id = data.get("partition_id", "")
168
180
  if "thermostat_name" in data:
169
- self.thermostat_name = data.get("thermostat_name")
181
+ self.thermostat_name = data.get("thermostat_name", "")
170
182
  if "device_temp_unit" in data:
171
- self.thermostat_device_temp_unit = data.get("device_temp_unit")
183
+ self.thermostat_device_temp_unit = data.get("device_temp_unit", "")
172
184
  if "current_temp" in data:
173
- self.thermostat_current_temp = data.get("current_temp")
185
+ self.thermostat_current_temp = data.get("current_temp", "")
174
186
  if "target_cool_temp" in data:
175
- self.thermostat_target_cool_temp = data.get("target_cool_temp")
187
+ self.thermostat_target_cool_temp = data.get("target_cool_temp", "")
176
188
  if "target_heat_temp" in data:
177
- self.thermostat_target_heat_temp = data.get("target_heat_temp")
189
+ self.thermostat_target_heat_temp = data.get("target_heat_temp", "")
178
190
  if "target_temp" in data:
179
- self.thermostat_target_temp = data.get("target_temp")
191
+ self.thermostat_target_temp = data.get("target_temp", "")
180
192
  if "power_usage" in data:
181
- self._thermostat_power_usage = data.get("power_usage")
193
+ self._thermostat_power_usage = data.get("power_usage", "")
182
194
  if "thermostat_mode" in data:
183
- self.thermostat_mode = data.get("thermostat_mode")
195
+ self.thermostat_mode = data.get("thermostat_mode", "")
184
196
  if "thermostat_mode_bitmask" in data:
185
- self._thermostat_mode_bitmask = data.get("thermostat_mode_bitmask")
197
+ self._thermostat_mode_bitmask = data.get("thermostat_mode_bitmask", "")
186
198
  if "fan_mode" in data:
187
- self.thermostat_fan_mode = data.get("fan_mode")
199
+ self.thermostat_fan_mode = data.get("fan_mode", "")
188
200
  if "fan_mode_bitmask" in data:
189
- self._thermostat_fan_mode_bitmask = data.get("fan_mode_bitmask")
201
+ self._thermostat_fan_mode_bitmask = data.get("fan_mode_bitmask", "")
190
202
  if "set_point_mode" in data:
191
- self.thermostat_set_point_mode = data.get("set_point_mode")
203
+ self.thermostat_set_point_mode = data.get("set_point_mode", "")
192
204
  if "set_point_mode_bitmask" in data:
193
- self._thermostat_set_point_mode_bitmask = data.get("set_point_mode_bitmask")
205
+ self._thermostat_set_point_mode_bitmask = data.get("set_point_mode_bitmask", "")
194
206
  if "created_by" in data:
195
- self._thermostat_created_by = data.get("created_by")
207
+ self._thermostat_created_by = data.get("created_by", "")
196
208
  if "updated_by" in data:
197
- self._thermostat_updated_by = data.get("updated_by")
209
+ self._thermostat_updated_by = data.get("updated_by", "")
198
210
  if "last_updated_date" in data:
199
- self._thermostat_last_updated_date = data.get("last_updated_date")
211
+ self._thermostat_last_updated_date = data.get("last_updated_date", "")
200
212
  if "thermostat_mode_updated_time" in data:
201
- self._thermostat_mode_updated_time = data.get("thermostat_mode_updated_time")
213
+ self._thermostat_mode_updated_time = data.get("thermostat_mode_updated_time", "")
202
214
  if "fan_mode_updated_time" in data:
203
- self._thermostat_fan_mode_updated_time = data.get("fan_mode_updated_time")
215
+ self._thermostat_fan_mode_updated_time = data.get("fan_mode_updated_time", "")
204
216
  if "set_point_mode_updated_time" in data:
205
- self._thermostat_set_point_mode_updated_time = data.get("set_point_mode_updated_time")
217
+ self._thermostat_set_point_mode_updated_time = data.get("set_point_mode_updated_time", "")
206
218
  if "target_cool_temp_updated_time" in data:
207
- self._thermostat_target_cool_temp_updated_time = data.get("target_cool_temp_updated_time")
219
+ self._thermostat_target_cool_temp_updated_time = data.get("target_cool_temp_updated_time", "")
208
220
  if "target_heat_temp_updated_time" in data:
209
- self._thermostat_target_heat_temp_updated_time = data.get("target_heat_temp_updated_time")
221
+ self._thermostat_target_heat_temp_updated_time = data.get("target_heat_temp_updated_time", "")
210
222
  if "current_temp_updated_time" in data:
211
- self._thermostat_current_temp_updated_time = data.get("current_temp_updated_time")
223
+ self._thermostat_current_temp_updated_time = data.get("current_temp_updated_time", "")
212
224
  if "paired_status" in data:
213
- self._thermostat_paired_status = data.get("paired_status")
225
+ self._thermostat_paired_status = data.get("paired_status", "")
214
226
  if "endpoint" in data:
215
- self._thermostat_endpoint = data.get("endpoint")
227
+ self._thermostat_endpoint = data.get("endpoint", "")
216
228
  if "configuration_parameter" in data:
217
- self._thermostat_configuration_parameter = data.get("configuration_parameter")
229
+ self._thermostat_configuration_parameter = data.get("configuration_parameter", "")
218
230
 
219
231
  self.end_batch_update()
220
232
 
221
- def to_dict_thermostat(self) -> dict:
233
+ def to_dict_thermostat(self) -> dict[str, str]:
222
234
  return {
223
235
  "_id": self._thermostat_id,
224
236
  "version": self._thermostat_version,
@@ -231,9 +243,9 @@ class QolsysThermostat(QolsysZWaveDevice):
231
243
  "target_heat_temp": self.thermostat_target_heat_temp,
232
244
  "target_temp": self.thermostat_target_temp,
233
245
  "power_usage": self._thermostat_power_usage,
234
- "thermostat_mode": self.thermostat_mode,
246
+ "thermostat_mode": self._thermostat_mode,
235
247
  "thermostat_mode_bitmask": self._thermostat_mode_bitmask,
236
- "fan_mode": self.thermostat_fan_mode,
248
+ "fan_mode": self._thermostat_fan_mode,
237
249
  "fan_mode_bitmask": self._thermostat_fan_mode_bitmask,
238
250
  "set_point_mode": self.thermostat_set_point_mode,
239
251
  "set_point_mode_bitmask": self._thermostat_set_point_mode_bitmask,
@@ -251,15 +263,7 @@ class QolsysThermostat(QolsysZWaveDevice):
251
263
  "configuration_parameter": self._thermostat_configuration_parameter,
252
264
  }
253
265
 
254
- def thermostat_mode(self) -> ThermostatMode:
255
- thermostat_mode = int(self._thermostat_mode)
256
- for mode in ThermostatMode:
257
- if thermostat_mode == mode:
258
- return mode
259
- return None
260
-
261
266
  def available_thermostat_mode(self) -> list[ThermostatMode]:
262
-
263
267
  int_list = [int(x) for x in self._thermostat_mode_bitmask.split(",")]
264
268
  byte_array = bytes(int_list)
265
269
  bitmask = int.from_bytes(byte_array, byteorder="little")
@@ -271,15 +275,7 @@ class QolsysThermostat(QolsysZWaveDevice):
271
275
 
272
276
  return mode_array
273
277
 
274
- def thermostat_fan_mode(self) -> ThermostatFanMode:
275
- thermostat_fan_mode = int(self._thermostat_fan_mode)
276
- for mode in ThermostatMode:
277
- if thermostat_fan_mode == mode:
278
- return mode
279
- return None
280
-
281
278
  def available_thermostat_fan_mode(self) -> list[ThermostatFanMode]:
282
-
283
279
  int_list = [int(x) for x in self._thermostat_fan_mode_bitmask.split(",")]
284
280
  byte_array = bytes(int_list)
285
281
  bitmask = int.from_bytes(byte_array, byteorder="little")
@@ -292,7 +288,6 @@ class QolsysThermostat(QolsysZWaveDevice):
292
288
  return fan_mode_array
293
289
 
294
290
  def available_thermostat_set_point_mode(self) -> list[ThermostatMode]:
295
-
296
291
  int_list = [int(x) for x in self._thermostat_set_point_mode_bitmask.split(",")]
297
292
  byte_array = bytes(int_list)
298
293
  bitmask = int.from_bytes(byte_array, byteorder="little")
@@ -303,4 +298,3 @@ class QolsysThermostat(QolsysZWaveDevice):
303
298
  set_point_mode_array.append(mode)
304
299
 
305
300
  return set_point_mode_array
306
-
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: qolsys-controller
3
+ Version: 0.0.62
4
+ Summary: A Python module that emulates a virtual IQ Remote device, enabling full local control of a Qolsys IQ Panel
5
+ Project-URL: Homepage, https://github.com/EHylands/QolsysController
6
+ Project-URL: Issues, https://github.com/EHylands/QolsysController/issues
7
+ Project-URL: Repository, https://github.com/EHylands/QolsysController.git
8
+ Author: Eric Hylands
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Topic :: Home Automation
14
+ Requires-Python: >=3.12
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Qolsys Controller - qolsys-controller
18
+
19
+ [![Build](https://github.com/EHylands/QolsysController/actions/workflows/build.yml/badge.svg)](https://github.com/EHylands/QolsysController/actions/workflows/build.yml)
20
+
21
+ A Python module that emulates a virtual IQ Remote device, enabling full **local control** of a Qolsys IQ Panel over MQTT — no cloud access required.
22
+
23
+ ## QolsysController
24
+ - ✅ Connects directly to the **Qolsys Panel's local MQTT server as an IQ Remote**
25
+ - 🔐 Pairs by only using **Installer Code** (same procedure as standard IQ Remote pairing)
26
+ - 🔢 Supports **4-digit user codes**
27
+ - ⚠️ Uses a **custom local usercode database** — panel's internal user code verification process is not yet supported
28
+
29
+ ## ✨ Functionality Highlights
30
+
31
+ | Category | Feature | Status |
32
+ |------------------------|--------------------------------------|--------|
33
+ | **Panel** | Diagnostic Sensors | ✅ |
34
+ | | Panel Scenes | ✅ |
35
+ | | Weather Forecast | ✅ |
36
+ | | (Alarm.com Weather to Panel) | |
37
+ | **Partition** | Arming Status | ✅ |
38
+ | | Alarm State | ✅ |
39
+ | | Home Instant Arming | ✅ |
40
+ | | Home Silent Disarming (Firmware 4.6.1)| ✅ |
41
+ | | Set Exit Sounds | ✅ |
42
+ | | Set Entry Delay | ✅ |
43
+ | | TTS | 🛠️ |
44
+ | **Zones** | Sensor Status | ✅ |
45
+ | | Tamper State | ✅ |
46
+ | | Battery Level | ✅ |
47
+ | | Temperature (supported PowerG device)| ✅ |
48
+ | | Light (supported PowerG device) | ✅ |
49
+ | | Average dBm | ✅ |
50
+ | | Latest dBm | ✅ |
51
+ | **Z-Wave Devices** | Battery Level | ✅ |
52
+ | | Node Status | ✅ |
53
+ | | Control Generic Devices | TBD |
54
+ | **Z-Wave Dimmers** | Binary Switch | ✅ |
55
+ | | Multi Level Dimmer | ✅ |
56
+ | **Z-Wave Door Locks** | Lock, Unlock | ✅ |
57
+ | **Z-Wave Thermostats** | Read device status | ✅ |
58
+ | | Write device status | ✅ |
59
+ | **Z-Wave Garage Doors**| | 🛠️ |
60
+ | **Z-Wave Outlets** | | 🛠️ |
61
+
62
+
63
+ ## ⚠️ Certificate Warning
64
+
65
+ During pairing, the main panel issues **only one signed client certificate** per virtual IQ Remote. If any key files are lost or deleted, re-pairing may become impossible.
66
+
67
+ A new PKI, including a new private key, can be recreated under specific circumstances, though the precise conditions remain unknown at this time.
68
+
69
+ **Important:**
70
+ Immediately back up the following files from the `pki/` directory after initial pairing:
71
+
72
+ - `.key` (private key)
73
+ - `.cer` (certificate)
74
+ - `.csr` (certificate signing request)
75
+ - `.secure` (signed client certificate)
76
+ - `.qolsys` (Qolsys Panel public certificate)
77
+
78
+ Store these files securely.
79
+
80
+ ## 📦 Installation
81
+
82
+ ```bash
83
+ git clone https://github.com/EHylands/QolsysController.git
84
+ cd qolsys_controller
85
+ pip3.12 install -r requirements.txt
86
+
87
+ # Change panel_ip and plugin_in in main.py file
88
+ python3.12 example.py
89
+ ```
@@ -0,0 +1,69 @@
1
+ qolsys_controller/__init__.py,sha256=TkjMFBP9VNyGLC84JQZ_tTeD8nJImkGgPMkQFGr2428,88
2
+ qolsys_controller/controller.py,sha256=ItJrkcQbGp4bcSqFQnb7C1ir5Ug5lg4OgWvteX8WajE,35296
3
+ qolsys_controller/enum.py,sha256=X17NdDcLRgT5veYnvxaQiq6lc1aA7FGBAMd6UKL11-c,4465
4
+ qolsys_controller/enum_zwave.py,sha256=1EqLnrlWUlzfHDmpZ7YQCOzNlfwU9mphPOtYZSdrzU0,1908
5
+ qolsys_controller/errors.py,sha256=Zu-WrHu4VxvKveT0piMNyPQmZlkUShRVpM5f8tRBNZg,1215
6
+ qolsys_controller/mdns.py,sha256=fXBNPcu5mOD8Kw28G0Wh5P_Bv2AonTo77pXanlz1FYo,894
7
+ qolsys_controller/mqtt_command.py,sha256=UNeMSNVtorJMo3PGGeG64JbmF6TDZKiCvv5Y4Rus0BY,3612
8
+ qolsys_controller/mqtt_command_queue.py,sha256=soXwl9gwNKtFBRQXR7O6ZqY77ujSpPOS25rNZpkm8A4,1016
9
+ qolsys_controller/observable.py,sha256=msQR49MKh6wrJyD4_D4XJZzbbWqtTZZT945iX3xY7U0,1007
10
+ qolsys_controller/panel.py,sha256=iRhSZkBHbsGTgJI9DURU01SDV1GhejWCyR2V9bR8Ud8,44915
11
+ qolsys_controller/partition.py,sha256=pxrYPz5lOJPQZ85TBzgNSwOHsD4S8ukTkwt_K2UFJ30,9214
12
+ qolsys_controller/pki.py,sha256=5UUmIxpXSWe1lo1miE2OkhKRUUudjTE0uH6bnt_uxqQ,7566
13
+ qolsys_controller/scene.py,sha256=vbaOqmiqMxFLqbx0tRwKsIfKJ75PMUP_SmXO4HjWsQ8,2053
14
+ qolsys_controller/settings.py,sha256=lfqBmMeE5_iz-pnpUHxXzH8eRdNvKpjnj4fid9DDNvc,7721
15
+ qolsys_controller/state.py,sha256=L7l7K8cJnUMQnEsek1GJH0BC7elhcAJ1QHYRr-yvjVw,18612
16
+ qolsys_controller/task_manager.py,sha256=QzdouQeAoWiK31MzozeNB5QB4KKlAM3zwZHJMKDqu0g,1664
17
+ qolsys_controller/users.py,sha256=cSf7o8TyvZldz06_qXWf8pLx_GSic9B0yIWNGXrvDqA,481
18
+ qolsys_controller/utils_mqtt.py,sha256=BmBkDR_wwqnNZnvwWIqkG8t1ArIT85e3dP-AQifsuKQ,358
19
+ qolsys_controller/weather.py,sha256=BE3AEm_I6rA7Ft5oV6ZMTwf6InamwxqGROddCeSzj-w,1930
20
+ qolsys_controller/zone.py,sha256=sFY0FGLqZcxKpJXBJLNMFf5S9L0C6vO9EbTDsamxaI8,17470
21
+ qolsys_controller/zwave_device.py,sha256=da0zoWE-qhoX7XNwoaL4Jzb8v9EK4p02AyDr9WlIg3w,9981
22
+ qolsys_controller/zwave_dimmer.py,sha256=dKbmjISp-Rj7kZ3_27Oks-1KUTWdFPmkyLHa_0I5e_E,5438
23
+ qolsys_controller/zwave_garagedoor.py,sha256=SCYJNeaDkWTh4GByjYz-cajYxJpQBqeibXWLO3g-pkQ,188
24
+ qolsys_controller/zwave_generic.py,sha256=k5sGtZdrIxi-qI-soeWrnkb2Cmhr7gl353TKodBhTz8,237
25
+ qolsys_controller/zwave_lock.py,sha256=G5Y5hQh_WQ1HGOLjGc4DElS27vPeS4d2lEh9uLMI8qU,5262
26
+ qolsys_controller/zwave_outlet.py,sha256=hNU9gd8lAF1Gxf2LszsbAIfEsLyMLgE-yQwaKJyhpf8,184
27
+ qolsys_controller/zwave_thermostat.py,sha256=PY5Rogk5zE3ybuOWAzGDu40huzujgVdGiPCHSja5KCQ,14899
28
+ qolsys_controller/database/db.py,sha256=Uf0g0JaBAoOoxBiO6jAZV3nm5XhEuB8K5ZrO__zTrSs,16027
29
+ qolsys_controller/database/table.py,sha256=kFA8-X_PqD9Wu1dzHhoBRTWKdrGRCaS0AEKwUnUG_X4,7405
30
+ qolsys_controller/database/table_alarmedsensor.py,sha256=3Qlpoam0Kuuokec54IaZSfYV6_1azAYpd2sdPkbQLd8,806
31
+ qolsys_controller/database/table_automation.py,sha256=zcEdtOq2Ac_0sILO2biP1oW3rgMXP4mhYq-kmJlSWHQ,1286
32
+ qolsys_controller/database/table_country_locale.py,sha256=tktJfwXnTw4jRGKlpT2MpLJ6GO_SVPocxmMDQU3X798,1012
33
+ qolsys_controller/database/table_dashboard_msgs.py,sha256=ldlyCIO1DfaNztC-2BaReCGRf7wJorOgn9M4ycVYc2k,865
34
+ qolsys_controller/database/table_dimmerlight.py,sha256=zrrDTTF3IqtxzVcKm2LoNAv5JsBs3mZ_9vLHAgKiDXw,926
35
+ qolsys_controller/database/table_doorlock.py,sha256=Mv4jgLbgrLAFNs3VVrFl6GTSfwI3b_UQ5jobyRHUQkM,1009
36
+ qolsys_controller/database/table_eu_event.py,sha256=6ec7W244EGpArj7RmJ83YTfvOXKGghC1P6XCeAumUCA,801
37
+ qolsys_controller/database/table_heat_map.py,sha256=j0bypc4VynokNsWZ3MFvL-hp2QBehdSIjHs486lkplM,745
38
+ qolsys_controller/database/table_history.py,sha256=peT1ONgDdkThdMTj3yAZZjzYBYi3jZ1TrG9VSguC7EM,940
39
+ qolsys_controller/database/table_iqremotesettings.py,sha256=a1ui58IVsiDgbQ8AwvDrR5QS0Iyn0I8DN6Bi-7bO34o,748
40
+ qolsys_controller/database/table_iqrouter_network_config.py,sha256=lb0MGYux86266JUUqxYuaUYN2_swxuFtk8Rs-pNja4A,624
41
+ qolsys_controller/database/table_iqrouter_user_device.py,sha256=wi23UYunBhdu5a3SwBEihnx0kJuL9VvcHHFS-3TO_hk,612
42
+ qolsys_controller/database/table_master_slave.py,sha256=aEg1h027gWdt7lN2sfr46tLvHO2FIkPcuVA6eI_mjNg,1196
43
+ qolsys_controller/database/table_nest_device.py,sha256=sjR35naF5QIKPjcemCOgao-xnMiQO08cw1cnMFml3Mg,566
44
+ qolsys_controller/database/table_output_rules.py,sha256=hX8nvlD4v6PtNo9NPXi_lcH3kjxcNj-2CRC6GUDB5tw,570
45
+ qolsys_controller/database/table_partition.py,sha256=UFgwZQyK4LmNvlQ-19WDV0e4NX0pmDL4GEbRa06vdQA,671
46
+ qolsys_controller/database/table_pgm_outputs.py,sha256=llklK4ygIWDzE92x9I3n-eFu0Uwq3KBjTkDYfbmo5jc,566
47
+ qolsys_controller/database/table_powerg_device.py,sha256=6KlmW48qqDoX7RSbsabU7YEn1RLhehYPFwLlEhBw_nI,1224
48
+ qolsys_controller/database/table_qolsyssettings.py,sha256=zdzN-c7j58Paw_PfVMQvuVPsQSjUdtxs9-YaWWw2qJY,683
49
+ qolsys_controller/database/table_scene.py,sha256=JofECe00jKM38woRcOT9sMn4SotYn_j6rD3JcIPUFjc,844
50
+ qolsys_controller/database/table_sensor.py,sha256=oAc6v0qUEqI-_WpSrV7SBiQkZLjg3jbrXtoVtJcmOGc,1927
51
+ qolsys_controller/database/table_sensor_group.py,sha256=yR_h_J-9bVMg_9EcpPbrPSJqKFIpygzZpvwx3kDtlZo,631
52
+ qolsys_controller/database/table_shades.py,sha256=RfpUCW1NxTlD1lkm8kntApIIt0JIcODUA_GRLNi6wr0,548
53
+ qolsys_controller/database/table_smartsocket.py,sha256=VLA1QLvzGgTvY0oqhGX_dLzjSDhSxulYZDygnq0RvJM,569
54
+ qolsys_controller/database/table_state.py,sha256=6Vp8_C1D-KNXTOu015YTa2j1QFxvJ_pWotkf31q9CTk,680
55
+ qolsys_controller/database/table_tcc.py,sha256=m-3xq1Dsgw2xxq5zaXti14w0Bu3gZ9qSjr8P15KrBiI,564
56
+ qolsys_controller/database/table_thermostat.py,sha256=ocsEF_1jCi0zWbjfVEOq8jbuDypZqzngP6BgYhaeAks,1549
57
+ qolsys_controller/database/table_trouble_conditions.py,sha256=yWFFpF49umdlNDDIftfSuR6dyhsRMK7OcaYPOoysTe4,790
58
+ qolsys_controller/database/table_user.py,sha256=nxFhxBpvD20-vweoJPsgc7KyHPzIVVtJqHGKDIFS3vw,1133
59
+ qolsys_controller/database/table_virtual_device.py,sha256=755hiteYKE5Mq7DiynLassug8tMBScvn7kRM1KSeZmk,578
60
+ qolsys_controller/database/table_weather.py,sha256=zQkdEBaL1H554aLuNUktd8_FptBukkqrRQrZGXU0tp4,815
61
+ qolsys_controller/database/table_zigbee_device.py,sha256=NhogYQllus2E7Y3wzBjpHeE20xtrj_a72moPHN5EJX0,573
62
+ qolsys_controller/database/table_zwave_association_group.py,sha256=RoTQPBE6gSxYM8PYoUXvRqyH7TPiA5CeOgcsI12peHY,941
63
+ qolsys_controller/database/table_zwave_history.py,sha256=hrHPAR4cza6AYhyhVFge496uYm4ZeV8UnhWPK57uOXs,941
64
+ qolsys_controller/database/table_zwave_node.py,sha256=K-GeDXebSV2vvUABMaN0k1rhh3BukGy5GJt0NqKcEdc,2608
65
+ qolsys_controller/database/table_zwave_other.py,sha256=CEaROgb0g_RdluT9GbyebnS0cglzKs5AjvaQARGnu48,914
66
+ qolsys_controller-0.0.62.dist-info/METADATA,sha256=8WXX_kvZimlxd3do97CB3dloadc1COSR48mvu86bkcU,4600
67
+ qolsys_controller-0.0.62.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
68
+ qolsys_controller-0.0.62.dist-info/licenses/LICENSE,sha256=GBHv9eggdA5ablDMW1xiLzGDZ2gCIhcKGW__c2aVIOc,1069
69
+ qolsys_controller-0.0.62.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,34 +0,0 @@
1
- import logging
2
-
3
- from .observable import QolsysObservable
4
- from .panel import QolsysPanel
5
- from .settings import QolsysSettings
6
- from .state import QolsysState
7
-
8
- LOGGER = logging.getLogger(__name__)
9
-
10
-
11
- class QolsysPlugin:
12
- def __init__(self, state: QolsysState, panel: QolsysPanel, settings: QolsysSettings) -> None:
13
-
14
- self._state: QolsysState = state
15
- self._panel: QolsysPanel = panel
16
- self._settings: QolsysSettings = settings
17
-
18
- self.connected = False
19
- self.connected_observer = QolsysObservable()
20
-
21
- def config(self) -> None:
22
- pass
23
-
24
- @property
25
- def state(self) -> QolsysState:
26
- return self._state
27
-
28
- @property
29
- def panel(self) -> QolsysPanel:
30
- return self._panel
31
-
32
- @property
33
- def settings(self) -> QolsysSettings:
34
- return self._settings
@@ -1,17 +0,0 @@
1
- import logging
2
-
3
- from .plugin import QolsysPlugin
4
-
5
- LOGGER = logging.getLogger(__name__)
6
-
7
-
8
- class QolsysPluginC4(QolsysPlugin):
9
- def __init__(self) -> None:
10
-
11
- # C4 Integration
12
- self._token = ""
13
-
14
- def config(self, panel_ip: str, token: str) -> bool: # noqa: ARG002
15
- LOGGER.warning("C4Plugin: Configuring Plugin")
16
- super().config()
17
- return True