qolsys-controller 0.0.62__py3-none-any.whl → 0.0.87__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/adc_device.py +202 -0
- qolsys_controller/adc_service.py +139 -0
- qolsys_controller/adc_service_garagedoor.py +35 -0
- qolsys_controller/controller.py +245 -34
- qolsys_controller/database/db.py +60 -0
- qolsys_controller/database/table.py +1 -0
- qolsys_controller/database/table_alarmedsensor.py +2 -0
- qolsys_controller/database/table_history.py +2 -2
- qolsys_controller/database/table_smartsocket.py +12 -1
- qolsys_controller/database/table_thermostat.py +3 -0
- qolsys_controller/database/table_virtual_device.py +13 -1
- qolsys_controller/database/table_zwave_node.py +3 -0
- qolsys_controller/enum.py +5 -1
- qolsys_controller/enum_adc.py +28 -0
- qolsys_controller/enum_zwave.py +158 -29
- qolsys_controller/errors.py +5 -0
- qolsys_controller/mqtt_command.py +6 -0
- qolsys_controller/panel.py +109 -5
- qolsys_controller/partition.py +22 -2
- qolsys_controller/state.py +163 -1
- qolsys_controller/task_manager.py +4 -3
- qolsys_controller/zone.py +2 -1
- qolsys_controller/zwave_device.py +132 -4
- qolsys_controller/zwave_dimmer.py +3 -0
- qolsys_controller/zwave_energy_clamp.py +15 -0
- qolsys_controller/zwave_garagedoor.py +3 -0
- qolsys_controller/zwave_generic.py +3 -0
- qolsys_controller/zwave_lock.py +4 -0
- qolsys_controller/zwave_outlet.py +3 -0
- qolsys_controller/zwave_service_meter.py +192 -0
- qolsys_controller/zwave_service_multilevelsensor.py +119 -0
- qolsys_controller/zwave_thermometer.py +21 -0
- qolsys_controller/zwave_thermostat.py +150 -38
- {qolsys_controller-0.0.62.dist-info → qolsys_controller-0.0.87.dist-info}/METADATA +1 -1
- {qolsys_controller-0.0.62.dist-info → qolsys_controller-0.0.87.dist-info}/RECORD +37 -29
- {qolsys_controller-0.0.62.dist-info → qolsys_controller-0.0.87.dist-info}/WHEEL +0 -0
- {qolsys_controller-0.0.62.dist-info → qolsys_controller-0.0.87.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
__all__ = ["QolsysZwaveMeterSensor"]
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from enum import IntEnum
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Type
|
|
7
|
+
|
|
8
|
+
from qolsys_controller.enum_zwave import (
|
|
9
|
+
MeterRateType,
|
|
10
|
+
MeterType,
|
|
11
|
+
ZWaveElectricMeterScale,
|
|
12
|
+
ZWaveUnknownMeterScale,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from .zwave_device import QolsysZWaveDevice
|
|
17
|
+
|
|
18
|
+
LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_enum_by_name(enum: type[IntEnum], name: str) -> IntEnum | None:
|
|
22
|
+
for val in enum:
|
|
23
|
+
if val.name.upper() == name.upper():
|
|
24
|
+
return val
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class QolsysZwaveMeterSensor:
|
|
29
|
+
def __init__(self, parent_device: "QolsysZWaveDevice", parent_meter: "QolsysZwaveServiceMeter", scale: IntEnum) -> None:
|
|
30
|
+
self._parent_device: QolsysZWaveDevice = parent_device
|
|
31
|
+
self._parent_meter: QolsysZwaveServiceMeter = parent_meter
|
|
32
|
+
self._scale: IntEnum = scale
|
|
33
|
+
self._value: float | None = None
|
|
34
|
+
self._delta_time: int | None = None
|
|
35
|
+
self._previous_value: float | None = None
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def value(self) -> float | None:
|
|
39
|
+
return self._value
|
|
40
|
+
|
|
41
|
+
@value.setter
|
|
42
|
+
def value(self, new_value: float | None) -> None:
|
|
43
|
+
if self._value != new_value:
|
|
44
|
+
self._value = new_value
|
|
45
|
+
LOGGER.debug(
|
|
46
|
+
"ZWaveMeter%s-%s (%s) - %s - value: %s (%s)",
|
|
47
|
+
self._parent_device.node_id,
|
|
48
|
+
self._parent_meter.endpoint,
|
|
49
|
+
self._parent_device.node_name,
|
|
50
|
+
self._parent_meter._meter_type.name,
|
|
51
|
+
new_value,
|
|
52
|
+
self._scale.name,
|
|
53
|
+
)
|
|
54
|
+
self._parent_device.notify()
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def scale(self) -> IntEnum:
|
|
58
|
+
return self._scale
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class QolsysZwaveServiceMeter:
|
|
62
|
+
def __init__(self, parent_device: "QolsysZWaveDevice", endpoint: str, meter_dict: dict[str, Any]) -> None:
|
|
63
|
+
self._parent_device: QolsysZWaveDevice = parent_device
|
|
64
|
+
self._endpoint: str = endpoint
|
|
65
|
+
self._meter_type: MeterType = MeterType.UNKNOWN
|
|
66
|
+
self._rate_type: MeterRateType = MeterRateType.UNSPECIFIED
|
|
67
|
+
self._scale_type: Type[IntEnum] = ZWaveUnknownMeterScale
|
|
68
|
+
self._supported_scale: list[IntEnum] = []
|
|
69
|
+
self._sensors: list[QolsysZwaveMeterSensor] = []
|
|
70
|
+
self._master_reset_flag: bool = False
|
|
71
|
+
|
|
72
|
+
# Set Meter_Type
|
|
73
|
+
type: str | int = meter_dict.get("meter_type", "")
|
|
74
|
+
if type == "ENERGY_METER" or type == MeterType.ELECTRIC_METER.value:
|
|
75
|
+
self._meter_type = MeterType.ELECTRIC_METER
|
|
76
|
+
self._scale_type = ZWaveElectricMeterScale
|
|
77
|
+
else:
|
|
78
|
+
LOGGER.warning("Zave Meter Service - Unknown Meter Type: %s", type)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
# Set Rate Type
|
|
82
|
+
rate_type: int = meter_dict.get("meter_ratetype_supported", -1)
|
|
83
|
+
try:
|
|
84
|
+
self._rate_type = MeterRateType(rate_type)
|
|
85
|
+
except ValueError:
|
|
86
|
+
LOGGER.error("Zave Meter Service - Unknown MeterRateType, Setting to UNSPECIFIED")
|
|
87
|
+
self._rate_type = MeterRateType.UNSPECIFIED
|
|
88
|
+
|
|
89
|
+
# Set Master Reset Flag
|
|
90
|
+
self._master_reset_flag = bool(meter_dict.get("meter_master_reset_flag", False))
|
|
91
|
+
|
|
92
|
+
# Set Supported Scales
|
|
93
|
+
self.supported_scale = meter_dict.get("meter_scale_supported", "[]")
|
|
94
|
+
|
|
95
|
+
# Create Sensors for Supported Scales
|
|
96
|
+
for scale in self._supported_scale:
|
|
97
|
+
qolsys_meter_sensor = QolsysZwaveMeterSensor(self._parent_device, self, scale)
|
|
98
|
+
self.add_sensor(qolsys_meter_sensor)
|
|
99
|
+
|
|
100
|
+
# Update sensor values
|
|
101
|
+
self.update_iq2medi(meter_dict)
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def sensors(self) -> list[QolsysZwaveMeterSensor]:
|
|
105
|
+
return self._sensors
|
|
106
|
+
|
|
107
|
+
def add_sensor(self, new_sensor: QolsysZwaveMeterSensor) -> None:
|
|
108
|
+
for sensor in self._sensors:
|
|
109
|
+
if sensor._scale == new_sensor._scale:
|
|
110
|
+
LOGGER.error("Error Adding Sensor, scale allready present")
|
|
111
|
+
return
|
|
112
|
+
self._sensors.append(new_sensor)
|
|
113
|
+
self._parent_device.notify()
|
|
114
|
+
|
|
115
|
+
def get_sensor(self, scale: IntEnum) -> QolsysZwaveMeterSensor | None:
|
|
116
|
+
for sensor in self._sensors:
|
|
117
|
+
if sensor._scale == scale:
|
|
118
|
+
return sensor
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def endpoint(self) -> str:
|
|
123
|
+
return self._endpoint
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def rate_type(self) -> MeterRateType:
|
|
127
|
+
return self._rate_type
|
|
128
|
+
|
|
129
|
+
@rate_type.setter
|
|
130
|
+
def rate_type(self, value: MeterRateType) -> None:
|
|
131
|
+
if self._rate_type != value:
|
|
132
|
+
self._rate_type = value
|
|
133
|
+
# LOGGER.debug("Zave Meter Service - rate_type: %s", value.name)
|
|
134
|
+
self._parent_device.notify()
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def supported_scale(self) -> list[IntEnum]:
|
|
138
|
+
return self._supported_scale
|
|
139
|
+
|
|
140
|
+
@supported_scale.setter
|
|
141
|
+
def supported_scale(self, value: str) -> None:
|
|
142
|
+
try:
|
|
143
|
+
scales = json.loads(value)
|
|
144
|
+
cleaned = [s.strip() for s in scales]
|
|
145
|
+
|
|
146
|
+
except json.JSONDecodeError:
|
|
147
|
+
self._supported_scale = []
|
|
148
|
+
LOGGER.error("Zave Meter Service - Error parsing meter_scale_supported, Setting to empty list")
|
|
149
|
+
|
|
150
|
+
for key in cleaned:
|
|
151
|
+
for scale in self._scale_type:
|
|
152
|
+
if key.lower() == scale.name.lower():
|
|
153
|
+
if scale not in self._supported_scale:
|
|
154
|
+
self._supported_scale.append(scale)
|
|
155
|
+
|
|
156
|
+
def update_iq2medi(self, data: dict[str, Any]) -> None:
|
|
157
|
+
# Update Z-Wave Meter Service
|
|
158
|
+
|
|
159
|
+
# Cannot change meter type once created
|
|
160
|
+
type: str | int = data.get("meter_type", "")
|
|
161
|
+
if type == "ENERGY_METER" or type == MeterType.ELECTRIC_METER:
|
|
162
|
+
if self._meter_type is not MeterType.ELECTRIC_METER:
|
|
163
|
+
LOGGER.error("Zave Meter Service - Cannot change Meter Type once created")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
self._parent_device.start_batch_update()
|
|
167
|
+
|
|
168
|
+
# Upate Rate Type
|
|
169
|
+
if "meter_ratetype_supported" in data:
|
|
170
|
+
rate_type: int = data.get("meter_ratetype_supported", -1)
|
|
171
|
+
try:
|
|
172
|
+
self.rate_type = MeterRateType(rate_type)
|
|
173
|
+
except ValueError:
|
|
174
|
+
LOGGER.error("Zave Meter Service - Unknown MeterRateType, Setting to UNSPECIFIED")
|
|
175
|
+
self.rate_type = MeterRateType.UNSPECIFIED
|
|
176
|
+
|
|
177
|
+
# Update Master Reset Flag
|
|
178
|
+
if "meter_master_reset_flag" in data:
|
|
179
|
+
self._master_reset_flag = bool(data.get("meter_master_reset_flag", False))
|
|
180
|
+
|
|
181
|
+
# Update Meter Values
|
|
182
|
+
if "meter_scale_reading_values" in data:
|
|
183
|
+
scale_values: dict[str, Any] = data.get("meter_scale_reading_values", {})
|
|
184
|
+
|
|
185
|
+
for key, value in scale_values.items():
|
|
186
|
+
temp = get_enum_by_name(self._scale_type, key.strip())
|
|
187
|
+
if temp in self._supported_scale:
|
|
188
|
+
sensor = self.get_sensor(temp)
|
|
189
|
+
if sensor is not None:
|
|
190
|
+
sensor.value = float(value)
|
|
191
|
+
|
|
192
|
+
self._parent_device.end_batch_update()
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
__all__ = ["QolsysZwaveMultilevelSensor"]
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from qolsys_controller.enum_zwave import ZWaveMultilevelSensorScale
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .zwave_device import QolsysZWaveDevice
|
|
11
|
+
|
|
12
|
+
LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_enum_by_name(enum: type[IntEnum], name: str) -> IntEnum | None:
|
|
16
|
+
for val in enum:
|
|
17
|
+
if val.name.upper() == name.upper():
|
|
18
|
+
return val
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QolsysZwaveMultilevelSensor:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
parent_device: "QolsysZWaveDevice",
|
|
26
|
+
parent_sensor: "QolsysZwaveServiceMultilevelSensor",
|
|
27
|
+
unit: ZWaveMultilevelSensorScale,
|
|
28
|
+
) -> None:
|
|
29
|
+
self._parent_device: QolsysZWaveDevice = parent_device
|
|
30
|
+
self._parent_meter: QolsysZwaveServiceMultilevelSensor = parent_sensor
|
|
31
|
+
self._value: float | None = None
|
|
32
|
+
self._unit: ZWaveMultilevelSensorScale = unit
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def unit(self) -> ZWaveMultilevelSensorScale:
|
|
36
|
+
return self._unit
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def value(self) -> float | None:
|
|
40
|
+
return self._value
|
|
41
|
+
|
|
42
|
+
@value.setter
|
|
43
|
+
def value(self, new_value: float | None) -> None:
|
|
44
|
+
if self._value != new_value:
|
|
45
|
+
self._value = new_value
|
|
46
|
+
LOGGER.debug(
|
|
47
|
+
"ZWaveMultilevelSensor%s-%s (%s) - value: %s (%s)",
|
|
48
|
+
self._parent_device.node_id,
|
|
49
|
+
self._parent_meter.endpoint,
|
|
50
|
+
self._parent_device.node_name,
|
|
51
|
+
new_value,
|
|
52
|
+
self._unit.name,
|
|
53
|
+
)
|
|
54
|
+
self._parent_device.notify()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class QolsysZwaveServiceMultilevelSensor:
|
|
58
|
+
def __init__(self, parent_device: "QolsysZWaveDevice", endpoint: str, sensor_dict: dict[str, Any]) -> None:
|
|
59
|
+
self._parent_device: QolsysZWaveDevice = parent_device
|
|
60
|
+
self._endpoint: str = endpoint
|
|
61
|
+
self._sensors: list[QolsysZwaveMultilevelSensor] = []
|
|
62
|
+
|
|
63
|
+
# Update sensor values
|
|
64
|
+
self.update_iq2medi(sensor_dict)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def sensors(self) -> list[QolsysZwaveMultilevelSensor]:
|
|
68
|
+
return self._sensors
|
|
69
|
+
|
|
70
|
+
def add_sensor(self, new_sensor: QolsysZwaveMultilevelSensor) -> None:
|
|
71
|
+
for sensor in self._sensors:
|
|
72
|
+
if sensor._unit == new_sensor._unit:
|
|
73
|
+
LOGGER.error("Error Adding Sensor, unit allready present")
|
|
74
|
+
return
|
|
75
|
+
self._sensors.append(new_sensor)
|
|
76
|
+
self._parent_device.notify()
|
|
77
|
+
|
|
78
|
+
def get_sensor(self, unit: ZWaveMultilevelSensorScale) -> QolsysZwaveMultilevelSensor | None:
|
|
79
|
+
for sensor in self._sensors:
|
|
80
|
+
if sensor.unit == unit:
|
|
81
|
+
return sensor
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def endpoint(self) -> str:
|
|
86
|
+
return self._endpoint
|
|
87
|
+
|
|
88
|
+
def update_iq2medi(self, data: dict[str, Any]) -> None:
|
|
89
|
+
# Update Z-Wave Multilevelsensor Service
|
|
90
|
+
|
|
91
|
+
self._parent_device.start_batch_update()
|
|
92
|
+
|
|
93
|
+
# Update Sensors Values
|
|
94
|
+
for key, value in data.items():
|
|
95
|
+
if key == "AIR TEMPERATURE":
|
|
96
|
+
temperature: float | None = value.get("Fahrenheit (F)", None)
|
|
97
|
+
sensor = self.get_sensor(ZWaveMultilevelSensorScale.TEMPERATURE_FAHRENHEIT)
|
|
98
|
+
if sensor:
|
|
99
|
+
sensor.value = temperature
|
|
100
|
+
else:
|
|
101
|
+
sensor = QolsysZwaveMultilevelSensor(
|
|
102
|
+
self._parent_device, self, ZWaveMultilevelSensorScale.TEMPERATURE_FAHRENHEIT
|
|
103
|
+
)
|
|
104
|
+
self.add_sensor(sensor)
|
|
105
|
+
sensor.value = temperature
|
|
106
|
+
|
|
107
|
+
if key == "HUMIDITY":
|
|
108
|
+
humidity: float | None = value.get("Percentage value (%)", None)
|
|
109
|
+
sensor = self.get_sensor(ZWaveMultilevelSensorScale.RELATIVE_HUMIDITY)
|
|
110
|
+
if sensor:
|
|
111
|
+
sensor.value = humidity
|
|
112
|
+
else:
|
|
113
|
+
sensor = QolsysZwaveMultilevelSensor(
|
|
114
|
+
self._parent_device, self, ZWaveMultilevelSensorScale.RELATIVE_HUMIDITY
|
|
115
|
+
)
|
|
116
|
+
self.add_sensor(sensor)
|
|
117
|
+
sensor.value = humidity
|
|
118
|
+
|
|
119
|
+
self._parent_device.end_batch_update()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .zwave_device import QolsysZWaveDevice
|
|
4
|
+
|
|
5
|
+
LOGGER = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QolsysThermometer(QolsysZWaveDevice):
|
|
9
|
+
def __init__(self, zwave_dict: dict[str, str]) -> None:
|
|
10
|
+
super().__init__(zwave_dict)
|
|
11
|
+
self._temprature_value = ""
|
|
12
|
+
self._humidity_value = ""
|
|
13
|
+
|
|
14
|
+
def update_raw(self, payload: bytes) -> None:
|
|
15
|
+
LOGGER.debug("Raw Update (node%s) - payload: %s", self.node_id, payload.hex())
|
|
16
|
+
|
|
17
|
+
def to_dict_thermometer(self) -> dict[str, str]:
|
|
18
|
+
return {
|
|
19
|
+
"temperature_value": self._temprature_value,
|
|
20
|
+
"humidity_value": self._humidity_value,
|
|
21
|
+
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from .enum_zwave import
|
|
3
|
+
from .enum_zwave import (
|
|
4
|
+
BITMASK_SUPPORTED_THERMOSTAT_FAN_MODE,
|
|
5
|
+
BITMASK_SUPPORTED_THERMOSTAT_MODE,
|
|
6
|
+
BITMASK_SUPPORTED_THERMOSTAT_SETPOINT_MODE,
|
|
7
|
+
ThermostatFanMode,
|
|
8
|
+
ThermostatMode,
|
|
9
|
+
ThermostatSetpointMode,
|
|
10
|
+
ZWaveMultilevelSensorScale,
|
|
11
|
+
)
|
|
4
12
|
from .zwave_device import QolsysZWaveDevice
|
|
5
13
|
|
|
6
14
|
LOGGER = logging.getLogger(__name__)
|
|
@@ -41,6 +49,10 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
41
49
|
self._thermostat_endpoint: str = thermostat_dict.get("endpoint", "")
|
|
42
50
|
self._thermostat_paired_status: str = thermostat_dict.get("paired_status", "")
|
|
43
51
|
self._thermostat_configuration_parameter: str = thermostat_dict.get("configuration_parameter", "")
|
|
52
|
+
self._thermostat_operating_state: str = thermostat_dict.get("operating_state", "")
|
|
53
|
+
self._thermostat_setpoint_capabilites = thermostat_dict.get("setpoint_capabilites", "")
|
|
54
|
+
|
|
55
|
+
self._thermostat_current_humidity: float | None = None
|
|
44
56
|
|
|
45
57
|
# -----------------------------
|
|
46
58
|
# properties + setters
|
|
@@ -61,6 +73,41 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
61
73
|
self._thermostat_name = value
|
|
62
74
|
self.notify()
|
|
63
75
|
|
|
76
|
+
@property
|
|
77
|
+
def thermostat_operating_state(self) -> str:
|
|
78
|
+
return self._thermostat_operating_state
|
|
79
|
+
|
|
80
|
+
@thermostat_operating_state.setter
|
|
81
|
+
def thermostat_operating_state(self, value: str) -> None:
|
|
82
|
+
if self._thermostat_operating_state != value:
|
|
83
|
+
LOGGER.debug("Thermostat%s (%s) - operating_state: %s", self.thermostat_node_id, self.thermostat_name, value)
|
|
84
|
+
self._thermostat_operating_state = value
|
|
85
|
+
self.notify()
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def thermostat_configuration_parameter(self) -> str:
|
|
89
|
+
return self._thermostat_configuration_parameter
|
|
90
|
+
|
|
91
|
+
@thermostat_configuration_parameter.setter
|
|
92
|
+
def thermostat_configuration_parameter(self, value: str) -> None:
|
|
93
|
+
if self._thermostat_configuration_parameter != value:
|
|
94
|
+
LOGGER.debug(
|
|
95
|
+
"Thermostat%s (%s) - configuration_parameter: %s", self.thermostat_node_id, self.thermostat_name, value
|
|
96
|
+
)
|
|
97
|
+
self._thermostat_configuration_parameter = value
|
|
98
|
+
self.notify()
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def thermostat_setpoint_capabilites(self) -> str:
|
|
102
|
+
return self._thermostat_setpoint_capabilites
|
|
103
|
+
|
|
104
|
+
@thermostat_setpoint_capabilites.setter
|
|
105
|
+
def thermostat_setpoint_capabilites(self, value: str) -> None:
|
|
106
|
+
if self._thermostat_setpoint_capabilites != value:
|
|
107
|
+
LOGGER.debug("Thermostat%s (%s) - setpoint_capabilites: %s", self.thermostat_node_id, self.thermostat_name, value)
|
|
108
|
+
self._thermostat_setpoint_capabilites = value
|
|
109
|
+
self.notify()
|
|
110
|
+
|
|
64
111
|
@property
|
|
65
112
|
def thermostat_device_temp_unit(self) -> str:
|
|
66
113
|
return self._thermostat_device_temp_unit
|
|
@@ -118,37 +165,79 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
118
165
|
|
|
119
166
|
@property
|
|
120
167
|
def thermostat_mode(self) -> ThermostatMode | None:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
168
|
+
value = self._thermostat_mode.strip("[]").split(",")
|
|
169
|
+
|
|
170
|
+
if len(value) > 1:
|
|
171
|
+
LOGGER.error(
|
|
172
|
+
"Thermostat%s (%s) - thermostat_mode has multiple values: %s",
|
|
173
|
+
self.thermostat_node_id,
|
|
174
|
+
self.thermostat_name,
|
|
175
|
+
value,
|
|
176
|
+
)
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
int_value = int(value[0])
|
|
181
|
+
for mode in ThermostatMode:
|
|
182
|
+
if int_value == mode.value:
|
|
183
|
+
return mode
|
|
184
|
+
return None
|
|
185
|
+
except ValueError:
|
|
186
|
+
LOGGER.error(
|
|
187
|
+
"Thermostat%s (%s) - thermostat_mode value is not an integer: %s",
|
|
188
|
+
self.thermostat_node_id,
|
|
189
|
+
self.thermostat_name,
|
|
190
|
+
value,
|
|
191
|
+
)
|
|
192
|
+
return None
|
|
126
193
|
|
|
127
194
|
@thermostat_mode.setter
|
|
128
195
|
def thermostat_mode(self, value: str) -> None:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
self.
|
|
196
|
+
new_value = value.strip("[]")
|
|
197
|
+
if self._thermostat_mode != new_value:
|
|
198
|
+
LOGGER.debug("Thermostat%s (%s) - mode: %s", self.thermostat_node_id, self.thermostat_name, new_value)
|
|
199
|
+
self._thermostat_mode = new_value
|
|
132
200
|
self.notify()
|
|
133
201
|
|
|
134
202
|
@property
|
|
135
203
|
def thermostat_fan_mode(self) -> ThermostatFanMode | None:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
204
|
+
value = self._thermostat_fan_mode.strip("[]").split(",")
|
|
205
|
+
|
|
206
|
+
if len(value) > 1:
|
|
207
|
+
LOGGER.error(
|
|
208
|
+
"Thermostat%s (%s) - thermostat_fan_mode has multiple values: %s",
|
|
209
|
+
self.thermostat_node_id,
|
|
210
|
+
self.thermostat_name,
|
|
211
|
+
value,
|
|
212
|
+
)
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
int_value = int(value[0])
|
|
217
|
+
for mode in ThermostatFanMode:
|
|
218
|
+
if int_value == mode.value:
|
|
219
|
+
return mode
|
|
220
|
+
return None
|
|
221
|
+
except ValueError:
|
|
222
|
+
LOGGER.error(
|
|
223
|
+
"Thermostat%s (%s) - thermostat_fan_mode value is not an integer: %s",
|
|
224
|
+
self.thermostat_node_id,
|
|
225
|
+
self.thermostat_name,
|
|
226
|
+
value,
|
|
227
|
+
)
|
|
228
|
+
return None
|
|
141
229
|
|
|
142
230
|
@thermostat_fan_mode.setter
|
|
143
231
|
def thermostat_fan_mode(self, value: str) -> None:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
self.
|
|
232
|
+
new_value = value.strip("[]")
|
|
233
|
+
if self._thermostat_fan_mode != new_value:
|
|
234
|
+
LOGGER.debug("Thermostat%s (%s) - fan_mode: %s", self.thermostat_node_id, self.thermostat_name, new_value)
|
|
235
|
+
self._thermostat_fan_mode = new_value
|
|
147
236
|
self.notify()
|
|
148
237
|
|
|
149
238
|
@property
|
|
150
239
|
def thermostat_set_point_mode(self) -> str:
|
|
151
|
-
return self._thermostat_set_point_mode
|
|
240
|
+
return self._thermostat_set_point_mode.strip("[]")
|
|
152
241
|
|
|
153
242
|
@thermostat_set_point_mode.setter
|
|
154
243
|
def thermostat_set_point_mode(self, value: str) -> None:
|
|
@@ -157,6 +246,22 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
157
246
|
self._thermostat_set_point_mode = value
|
|
158
247
|
self.notify()
|
|
159
248
|
|
|
249
|
+
@property
|
|
250
|
+
def thermostat_current_humidity(self) -> float | None:
|
|
251
|
+
sensor = self.multilevelsensor_value(ZWaveMultilevelSensorScale.RELATIVE_HUMIDITY)
|
|
252
|
+
|
|
253
|
+
if not sensor:
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
if len(sensor) == 1:
|
|
257
|
+
return sensor[0].value
|
|
258
|
+
|
|
259
|
+
LOGGER.error("Multiple humidity sensor present")
|
|
260
|
+
return sensor[0].value
|
|
261
|
+
|
|
262
|
+
def update_raw(self, payload: bytes) -> None:
|
|
263
|
+
pass
|
|
264
|
+
|
|
160
265
|
def update_thermostat(self, data: dict[str, str]) -> None: # noqa: C901, PLR0912, PLR0915
|
|
161
266
|
# Check if we are updating same none_id
|
|
162
267
|
node_id_update = data.get("node_id", "")
|
|
@@ -227,6 +332,10 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
227
332
|
self._thermostat_endpoint = data.get("endpoint", "")
|
|
228
333
|
if "configuration_parameter" in data:
|
|
229
334
|
self._thermostat_configuration_parameter = data.get("configuration_parameter", "")
|
|
335
|
+
if "operating_state" in data:
|
|
336
|
+
self.thermostat_operating_state = data.get("operating_state", "")
|
|
337
|
+
if "setpoint_capabilites" in data:
|
|
338
|
+
self.thermostat_setpoint_capabilites = data.get("setpoint_capabilites", "")
|
|
230
339
|
|
|
231
340
|
self.end_batch_update()
|
|
232
341
|
|
|
@@ -237,6 +346,7 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
237
346
|
"opr": self._thermostat_opr,
|
|
238
347
|
"partition_id": self._thermostat_partition_id,
|
|
239
348
|
"thermostat_name": self.thermostat_name,
|
|
349
|
+
"node_id": self.thermostat_node_id,
|
|
240
350
|
"device_temp_unit": self.thermostat_device_temp_unit,
|
|
241
351
|
"current_temp": self.thermostat_current_temp,
|
|
242
352
|
"target_cool_temp": self.thermostat_target_cool_temp,
|
|
@@ -260,41 +370,43 @@ class QolsysThermostat(QolsysZWaveDevice):
|
|
|
260
370
|
"current_temp_updated_time": self._thermostat_current_temp_updated_time,
|
|
261
371
|
"paired_status": self._thermostat_paired_status,
|
|
262
372
|
"endpoint": self._thermostat_endpoint,
|
|
263
|
-
"configuration_parameter": self.
|
|
373
|
+
"configuration_parameter": self.thermostat_configuration_parameter,
|
|
374
|
+
"operating_state": self.thermostat_operating_state,
|
|
375
|
+
"setpoint_capabilites": self._thermostat_setpoint_capabilites,
|
|
264
376
|
}
|
|
265
377
|
|
|
266
378
|
def available_thermostat_mode(self) -> list[ThermostatMode]:
|
|
267
|
-
int_list = [int(x) for x in self._thermostat_mode_bitmask.split(",")]
|
|
379
|
+
int_list = [int(x) for x in self._thermostat_mode_bitmask.strip("[]").split(",")]
|
|
268
380
|
byte_array = bytes(int_list)
|
|
269
381
|
bitmask = int.from_bytes(byte_array, byteorder="little")
|
|
270
382
|
|
|
271
|
-
|
|
272
|
-
for mode in
|
|
273
|
-
if
|
|
274
|
-
|
|
383
|
+
supported = []
|
|
384
|
+
for bit, mode in BITMASK_SUPPORTED_THERMOSTAT_MODE.items():
|
|
385
|
+
if bitmask & (1 << bit):
|
|
386
|
+
supported.append(mode)
|
|
275
387
|
|
|
276
|
-
return
|
|
388
|
+
return supported
|
|
277
389
|
|
|
278
390
|
def available_thermostat_fan_mode(self) -> list[ThermostatFanMode]:
|
|
279
|
-
int_list = [int(x) for x in self._thermostat_fan_mode_bitmask.split(",")]
|
|
391
|
+
int_list = [int(x) for x in self._thermostat_fan_mode_bitmask.strip("[]").split(",")]
|
|
280
392
|
byte_array = bytes(int_list)
|
|
281
393
|
bitmask = int.from_bytes(byte_array, byteorder="little")
|
|
282
394
|
|
|
283
|
-
|
|
284
|
-
for mode in
|
|
285
|
-
if
|
|
286
|
-
|
|
395
|
+
supported = []
|
|
396
|
+
for bit, mode in BITMASK_SUPPORTED_THERMOSTAT_FAN_MODE.items():
|
|
397
|
+
if bitmask & (1 << bit):
|
|
398
|
+
supported.append(mode)
|
|
287
399
|
|
|
288
|
-
return
|
|
400
|
+
return supported
|
|
289
401
|
|
|
290
|
-
def available_thermostat_set_point_mode(self) -> list[
|
|
291
|
-
int_list = [int(x) for x in self._thermostat_set_point_mode_bitmask.split(",")]
|
|
402
|
+
def available_thermostat_set_point_mode(self) -> list[ThermostatSetpointMode]:
|
|
403
|
+
int_list = [int(x) for x in self._thermostat_set_point_mode_bitmask.strip("[]").split(",")]
|
|
292
404
|
byte_array = bytes(int_list)
|
|
293
405
|
bitmask = int.from_bytes(byte_array, byteorder="little")
|
|
294
406
|
|
|
295
|
-
|
|
296
|
-
for mode in
|
|
297
|
-
if
|
|
298
|
-
|
|
407
|
+
supported = []
|
|
408
|
+
for bit, mode in BITMASK_SUPPORTED_THERMOSTAT_SETPOINT_MODE.items():
|
|
409
|
+
if bitmask & (1 << bit):
|
|
410
|
+
supported.append(mode)
|
|
299
411
|
|
|
300
|
-
return
|
|
412
|
+
return supported
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qolsys-controller
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.87
|
|
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
|