qolsys-controller 0.0.76__tar.gz → 0.0.81__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.
Files changed (88) hide show
  1. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/PKG-INFO +1 -1
  2. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/pyproject.toml +1 -1
  3. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/controller.py +91 -7
  4. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_thermostat.py +1 -0
  5. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_zwave_node.py +2 -0
  6. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/enum.py +1 -0
  7. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/enum_zwave.py +22 -0
  8. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/mqtt_command.py +6 -0
  9. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/panel.py +9 -9
  10. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/state.py +45 -21
  11. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_device.py +79 -7
  12. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_dimmer.py +1 -1
  13. qolsys_controller-0.0.81/qolsys_controller/zwave_energy_clamp.py +15 -0
  14. qolsys_controller-0.0.81/qolsys_controller/zwave_service_meter.py +192 -0
  15. qolsys_controller-0.0.81/qolsys_controller/zwave_service_multilevelsensor.py +119 -0
  16. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_thermostat.py +8 -14
  17. qolsys_controller-0.0.76/qolsys_controller/zwave_meter.py +0 -272
  18. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/.github/workflows/build.yml +0 -0
  19. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/.github/workflows/publish.yml +0 -0
  20. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/.gitignore +0 -0
  21. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/Info_mqtt.md +0 -0
  22. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/LICENSE +0 -0
  23. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/README.md +0 -0
  24. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/bin/qolsys.py +0 -0
  25. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/example.py +0 -0
  26. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/info_pairing.md +0 -0
  27. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/info_qolsys.md +0 -0
  28. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/mypy.ini +0 -0
  29. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/__init__.py +0 -0
  30. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/adc_device.py +0 -0
  31. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/adc_service.py +0 -0
  32. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/adc_service_garagedoor.py +0 -0
  33. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/db.py +0 -0
  34. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table.py +0 -0
  35. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_alarmedsensor.py +0 -0
  36. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_automation.py +0 -0
  37. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_country_locale.py +0 -0
  38. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_dashboard_msgs.py +0 -0
  39. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_dimmerlight.py +0 -0
  40. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_doorlock.py +0 -0
  41. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_eu_event.py +0 -0
  42. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_heat_map.py +0 -0
  43. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_history.py +0 -0
  44. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_iqremotesettings.py +0 -0
  45. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_iqrouter_network_config.py +0 -0
  46. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_iqrouter_user_device.py +0 -0
  47. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_master_slave.py +0 -0
  48. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_nest_device.py +0 -0
  49. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_output_rules.py +0 -0
  50. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_partition.py +0 -0
  51. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_pgm_outputs.py +0 -0
  52. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_powerg_device.py +0 -0
  53. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_qolsyssettings.py +0 -0
  54. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_scene.py +0 -0
  55. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_sensor.py +0 -0
  56. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_sensor_group.py +0 -0
  57. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_shades.py +0 -0
  58. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_smartsocket.py +0 -0
  59. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_state.py +0 -0
  60. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_tcc.py +0 -0
  61. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_trouble_conditions.py +0 -0
  62. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_user.py +0 -0
  63. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_virtual_device.py +0 -0
  64. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_weather.py +0 -0
  65. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_zigbee_device.py +0 -0
  66. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_zwave_association_group.py +0 -0
  67. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_zwave_history.py +0 -0
  68. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/database/table_zwave_other.py +0 -0
  69. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/enum_adc.py +0 -0
  70. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/errors.py +0 -0
  71. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/mdns.py +0 -0
  72. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/mqtt_command_queue.py +0 -0
  73. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/observable.py +0 -0
  74. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/partition.py +0 -0
  75. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/pki.py +0 -0
  76. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/scene.py +0 -0
  77. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/settings.py +0 -0
  78. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/task_manager.py +0 -0
  79. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/users.py +0 -0
  80. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/utils_mqtt.py +0 -0
  81. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/weather.py +0 -0
  82. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zone.py +0 -0
  83. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_garagedoor.py +0 -0
  84. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_generic.py +0 -0
  85. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_lock.py +0 -0
  86. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_outlet.py +0 -0
  87. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/qolsys_controller/zwave_thermometer.py +0 -0
  88. {qolsys_controller-0.0.76 → qolsys_controller-0.0.81}/requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qolsys-controller
3
- Version: 0.0.76
3
+ Version: 0.0.81
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "qolsys-controller"
3
- version = "0.0.76"
3
+ version = "0.0.81"
4
4
  authors = [
5
5
  { name="Eric Hylands", email="" },
6
6
  ]
@@ -18,6 +18,7 @@ from qolsys_controller.mqtt_command import (
18
18
  MQTTCommand_Panel,
19
19
  MQTTCommand_ZWave,
20
20
  )
21
+ from qolsys_controller.zwave_thermostat import QolsysThermostat
21
22
 
22
23
  from .enum import PartitionAlarmState, PartitionArmingType, PartitionSystemStatus
23
24
  from .enum_zwave import ThermostatFanMode, ThermostatMode, ZwaveCommandClass, ZwaveDeviceClass
@@ -894,20 +895,103 @@ class QolsysController:
894
895
  return response
895
896
 
896
897
  async def command_zwave_thermostat_setpoint_set(
897
- self, node_id: str, mode: ThermostatMode, setpoint: float
898
+ self, node_id: str, mode: ThermostatMode, setpoint: int
898
899
  ) -> dict[str, Any] | None:
899
- LOGGER.debug(
900
- "MQTT: Sending zwave_thermostat_setpoint_set - Node(%s) - Mode(%s) - Setpoint(%s)", node_id, mode, setpoint
901
- )
902
-
903
900
  zwave_node = self.state.zwave_device(node_id)
904
901
  if not zwave_node:
905
902
  LOGGER.error("thermostat_setpoint_set - Invalid node_id %s", node_id)
906
903
  return None
907
904
 
908
- command = MQTTCommand_ZWave(self, node_id, [ZwaveCommandClass.ThermostatSetPoint, 1, mode, int(setpoint)])
905
+ if not isinstance(zwave_node, QolsysThermostat):
906
+ LOGGER.error("thermostat_setpoint_set - Z-Wane node is not a thermostat %s", node_id)
907
+ return None
908
+
909
+ scale: int = 0
910
+ if zwave_node.thermostat_device_temp_unit == "F":
911
+ scale = 1
912
+
913
+ precision: int = 1
914
+ size: int = 2
915
+ pss = (precision << 5) | (scale << 3) | size
916
+ temp_int = int(round(setpoint * (10**precision)))
917
+ temp_bytes = temp_int.to_bytes(size, byteorder="big", signed=True)
918
+
919
+ zwave_bytes: list[int] = [
920
+ 0x43, # Thermostat Setpoint
921
+ 0x03, # SET
922
+ mode.value, # Heating
923
+ pss,
924
+ ] + list(temp_bytes)
925
+
926
+ zwave_bytes2: list[int] = [
927
+ 0x43, # Thermostat Setpoint
928
+ 0x01, # SET
929
+ mode.value, # Heating
930
+ pss,
931
+ ] + list(temp_bytes)
932
+
933
+ zwave_bytes3: list[int] = [
934
+ 0x43, # Thermostat Setpoint
935
+ 0x01, # SET
936
+ 0x03, # Furnace
937
+ pss,
938
+ ] + list(temp_bytes)
939
+
940
+ zwave_bytes4: list[int] = [
941
+ 0x43, # Thermostat Setpoint
942
+ 0x03, # SET
943
+ 0x03, # Furnace
944
+ pss,
945
+ ] + list(temp_bytes)
946
+
947
+ LOGGER.debug(
948
+ "MQTT: Sending zwave_thermostat_setpoint_set 0x03 - Node(%s) - Mode(%s) - Setpoint(%s): %s",
949
+ node_id,
950
+ mode,
951
+ setpoint,
952
+ zwave_bytes,
953
+ )
954
+ command = MQTTCommand_ZWave(self, node_id, zwave_bytes)
909
955
  response = await command.send_command()
910
- LOGGER.debug("MQTT: Receiving zwave_thermostat_mode_set command")
956
+ LOGGER.debug("MQTT: Receiving zwave_thermostat_mode_set command:%s", response)
957
+
958
+ await asyncio.sleep(3)
959
+
960
+ LOGGER.debug(
961
+ "MQTT: Sending zwave_thermostat_setpoint_set 0x01 - Node(%s) - Mode(%s) - Setpoint(%s): %s",
962
+ node_id,
963
+ mode,
964
+ setpoint,
965
+ zwave_bytes2,
966
+ )
967
+ command2 = MQTTCommand_ZWave(self, node_id, zwave_bytes2)
968
+ response2 = await command2.send_command()
969
+ LOGGER.debug("MQTT: Receiving zwave_thermostat_mode_set command:%s", response2)
970
+
971
+ await asyncio.sleep(3)
972
+
973
+ LOGGER.debug(
974
+ "MQTT: Sending zwave_thermostat_setpoint_set 0x01 - Node(%s) - Mode(0x03) - Setpoint(%s): %s",
975
+ node_id,
976
+ setpoint,
977
+ zwave_bytes3,
978
+ )
979
+ command2 = MQTTCommand_ZWave(self, node_id, zwave_bytes2)
980
+ response2 = await command2.send_command()
981
+ LOGGER.debug("MQTT: Receiving zwave_thermostat_mode_set command:%s", response2)
982
+
983
+ await asyncio.sleep(3)
984
+
985
+ LOGGER.debug(
986
+ "MQTT: Sending zwave_thermostat_setpoint_set 0x03 - Node(%s) - Mode(0x3) - Setpoint(%s): %s",
987
+ node_id,
988
+ setpoint,
989
+ zwave_bytes4,
990
+ )
991
+ command2 = MQTTCommand_ZWave(self, node_id, zwave_bytes2)
992
+ response2 = await command2.send_command()
993
+ LOGGER.debug("MQTT: Receiving zwave_thermostat_mode_set command:%s", response2)
994
+
911
995
  return response
912
996
 
913
997
  async def command_zwave_thermostat_mode_set(self, node_id: str, mode: ThermostatMode) -> dict[str, Any] | None:
@@ -49,6 +49,7 @@ class QolsysTableThermostat(QolsysTable):
49
49
  "paired_status",
50
50
  "configuration_parameter",
51
51
  "operating_state",
52
+ "fan_state",
52
53
  ]
53
54
 
54
55
  self._create_table()
@@ -80,6 +80,8 @@ class QolsysTableZwaveNode(QolsysTable):
80
80
  "long_range_nodeid",
81
81
  "hide_device_info",
82
82
  "meter_capabilities",
83
+ "multisensor_capabilities",
84
+ "central_scene_supported",
83
85
  ]
84
86
 
85
87
  self._create_table()
@@ -64,6 +64,7 @@ class ZoneStatus(StrEnum):
64
64
  FAILURE = "Failure"
65
65
  NOT_NETWORKED = "Not Networked"
66
66
  DISARM = "Disarm"
67
+ ARM_AWAY = "Arm-Away"
67
68
 
68
69
 
69
70
  class DeviceCapability(StrEnum):
@@ -18,6 +18,28 @@ class MeterRateType(IntEnum):
18
18
  RESERVED = 0x03
19
19
 
20
20
 
21
+ class ZWaveMultilevelSensorScale(Enum):
22
+ TEMPERATURE_CELSIUS = "temperature_celsius"
23
+ TEMPERATURE_FAHRENHEIT = "temperature_fahrenheit"
24
+ RELATIVE_HUMIDITY = "relative_humidity"
25
+ WIND_DIRECTION = "wind_direction"
26
+ BAROMETRIC_PRESSURE = "barometric_pressure"
27
+ DEW_POINT = "dew_point"
28
+ RAIN_RATE = "rain_rate"
29
+ TIDE_LEVEL = "tide_level"
30
+ WEIGHT = "weight"
31
+ VOLTS = "volts"
32
+ AMPS = "amps"
33
+ WATTS = "watts"
34
+ DISTANCE = "distance"
35
+ ANGLE_POSITION = "angle_position"
36
+ ROTATION = "rotation"
37
+ WATER_TEMPERATURE_CELSIUS = "water_temperature_celsius"
38
+ WATER_TEMPERATURE_FAHRENHEIT = "water_temperature_fahrenheit"
39
+ LUMINOSITY_LUX = "luminosity_lux"
40
+ UNKNOWN = "unknown"
41
+
42
+
21
43
  class ZWaveUnknownMeterScale(IntEnum):
22
44
  UNKNOWN = 0
23
45
 
@@ -91,26 +91,32 @@ class MQTTCommand_ZWave(MQTTCommand_IpcCall):
91
91
 
92
92
  ipc_request: list[dict[str, Any]] = [
93
93
  {
94
+ # Node ID
94
95
  "dataType": "int",
95
96
  "dataValue": int(node_id),
96
97
  },
97
98
  {
99
+ # End Point
98
100
  "dataType": "int",
99
101
  "dataValue": 0,
100
102
  },
101
103
  {
104
+ # Z-Wave Payload
102
105
  "dataType": "byteArray",
103
106
  "dataValue": zwave_command,
104
107
  },
105
108
  {
109
+ # Transmit option ?
106
110
  "dataType": "int",
107
111
  "dataValue": 0,
108
112
  },
109
113
  {
114
+ # Priority
110
115
  "dataType": "int",
111
116
  "dataValue": 106,
112
117
  },
113
118
  {
119
+ # Callback ?
114
120
  "dataType": "byteArray",
115
121
  "dataValue": [0],
116
122
  },
@@ -6,7 +6,7 @@ import logging
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from qolsys_controller.adc_device import QolsysAdcDevice
9
- from qolsys_controller.zwave_meter import QolsysMeterDevice
9
+ from qolsys_controller.zwave_energy_clamp import QolsysEnergyClamp
10
10
  from qolsys_controller.zwave_thermometer import QolsysThermometer
11
11
 
12
12
  from .database.db import QolsysDB
@@ -427,12 +427,12 @@ class QolsysPanel(QolsysObservable):
427
427
  def parse_zwave_message(self, data: dict[str, Any]) -> None:
428
428
  zwave = data.get("ZWAVE_RESPONSE", "")
429
429
  payload = base64.b64decode(zwave.get("ZWAVE_PAYLOAD", "")).hex()
430
- LOGGER.debug(
431
- "Z-Wave Response: Node(%s) - Status(%s) - Payload(%s)",
432
- zwave.get("NODE_ID", ""),
433
- zwave.get("ZWAVE_COMMAND_STATUS", ""),
434
- payload,
435
- )
430
+ # LOGGER.debug(
431
+ # "Z-Wave Response: Node(%s) - Status(%s) - Payload(%s)",
432
+ # zwave.get("NODE_ID", ""),
433
+ # zwave.get("ZWAVE_COMMAND_STATUS", ""),
434
+ # payload,
435
+ # )
436
436
 
437
437
  node_id: str = str(zwave.get("NODE_ID", 0))
438
438
  node = self._controller.state.zwave_device(node_id)
@@ -900,8 +900,8 @@ class QolsysPanel(QolsysObservable):
900
900
 
901
901
  # Check if z-wave device is an Energy Clamp
902
902
  if device.get("node_type", "") == "Energy Clamp":
903
- qolsys_meter_device = QolsysMeterDevice(device)
904
- qolsys_meter_device.create_generic_electric_meter()
903
+ LOGGER.debug(device)
904
+ qolsys_meter_device = QolsysEnergyClamp(device)
905
905
  devices.append(qolsys_meter_device)
906
906
  device_added = True
907
907
 
@@ -1,12 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from enum import IntEnum
5
4
  from typing import TYPE_CHECKING
6
5
 
7
6
  from qolsys_controller.adc_service import QolsysAdcService
8
7
  from qolsys_controller.adc_service_garagedoor import QolsysAdcGarageDoorService
9
- from qolsys_controller.zwave_meter import QolsysMeterDevice
8
+ from qolsys_controller.zwave_energy_clamp import QolsysEnergyClamp
10
9
  from qolsys_controller.zwave_thermometer import QolsysThermometer
11
10
 
12
11
  from .adc_device import QolsysAdcDevice
@@ -96,10 +95,10 @@ class QolsysState(QolsysObservable):
96
95
  return thermostats
97
96
 
98
97
  @property
99
- def zwave_meters(self) -> list[QolsysMeterDevice]:
98
+ def zwave_meters(self) -> list[QolsysEnergyClamp]:
100
99
  meters = []
101
100
  for device in self.zwave_devices:
102
- if isinstance(device, QolsysMeterDevice):
101
+ if isinstance(device, QolsysEnergyClamp):
103
102
  meters.append(device)
104
103
  return meters
105
104
 
@@ -351,10 +350,9 @@ class QolsysState(QolsysObservable):
351
350
  state_zwave.update_lock(db_zwave.to_dict_lock())
352
351
  break
353
352
 
354
- # Update Meter
355
- if isinstance(state_zwave, QolsysMeterDevice) and isinstance(db_zwave, QolsysMeterDevice):
356
- # Meter state not store in database
357
- # Keep old value instact
353
+ # Update Energy Clamp
354
+ if isinstance(state_zwave, QolsysEnergyClamp) and isinstance(db_zwave, QolsysEnergyClamp):
355
+ state_zwave.update_base(db_zwave.to_dict_base())
358
356
  break
359
357
 
360
358
  # Generic Z-Wave Device
@@ -473,6 +471,29 @@ class QolsysState(QolsysObservable):
473
471
  def dump(self) -> None: # noqa: PLR0912, PLR0915
474
472
  LOGGER.debug("*** Device Information ***")
475
473
 
474
+ def dump_meter(self: QolsysState, device: QolsysZWaveDevice) -> None:
475
+ for endpoint in device.meter_endpoints:
476
+ for meter_sensor in endpoint.sensors:
477
+ LOGGER.debug(
478
+ " Meter%s Endpoint%s - %s - value: %.2f (%s)",
479
+ device.node_id,
480
+ endpoint.endpoint,
481
+ endpoint._meter_type.name,
482
+ meter_sensor.value,
483
+ meter_sensor.scale.name,
484
+ )
485
+
486
+ def dump_multilevelsensor(self: QolsysState, device: QolsysZWaveDevice) -> None:
487
+ for endpoint in device.multilevelsensor_endpoints:
488
+ for sensor in endpoint.sensors:
489
+ LOGGER.debug(
490
+ " Multilevelsensor%s Endpoint%s - value: %.2f (%s)",
491
+ device.node_id,
492
+ endpoint.endpoint,
493
+ sensor.value,
494
+ sensor.unit.name,
495
+ )
496
+
476
497
  for partition in self.partitions:
477
498
  pid = partition.id
478
499
  name = partition.name
@@ -513,6 +534,8 @@ class QolsysState(QolsysObservable):
513
534
  LOGGER.debug("Dimmer%s (%s) - node_status: %s", nid, name, zwave.node_status)
514
535
  LOGGER.debug("Dimmer%s (%s) - battery_level: %s", nid, name, zwave.node_battery_level)
515
536
  LOGGER.debug("Dimmer%s (%s) - battery_level_value: %s", nid, name, zwave.node_battery_level_value)
537
+ dump_meter(self, zwave)
538
+ dump_multilevelsensor(self, zwave)
516
539
  continue
517
540
 
518
541
  if isinstance(zwave, QolsysThermostat):
@@ -525,34 +548,33 @@ class QolsysState(QolsysObservable):
525
548
  LOGGER.debug("Thermostat%s (%s) - target_cool_temp: %s", zid, name, zwave.thermostat_target_cool_temp)
526
549
  LOGGER.debug("Thermostat%s (%s) - target_heat_temp: %s", zid, name, zwave.thermostat_target_heat_temp)
527
550
  LOGGER.debug("Thermostat%s (%s) - set_point_mode: %s", zid, name, zwave.thermostat_set_point_mode)
551
+ dump_meter(self, zwave)
552
+ dump_multilevelsensor(self, zwave)
528
553
  continue
529
554
 
530
555
  if isinstance(zwave, QolsysLock):
531
556
  zid = zwave.lock_node_id
532
557
  name = zwave.lock_name
533
558
  LOGGER.debug("Lock%s (%s) - lock_status: %s", zid, name, zwave.lock_status)
559
+ dump_meter(self, zwave)
560
+ dump_multilevelsensor(self, zwave)
534
561
  continue
535
562
 
536
- if isinstance(zwave, QolsysMeterDevice):
563
+ if isinstance(zwave, QolsysEnergyClamp):
537
564
  nid = zwave.node_id
538
565
  name = zwave.node_name
539
- LOGGER.debug("Meter%s (%s)", nid, name)
540
-
541
- for meter_sensor in zwave.meters:
542
- scale_type: type[IntEnum] = zwave.scale_for_meter_type(meter_sensor.meter_type)
543
-
544
- LOGGER.debug(
545
- " Sensor%s (%s) - value: %s (%s)",
546
- meter_sensor.meter_type,
547
- meter_sensor.meter_type.name,
548
- meter_sensor.value,
549
- scale_type(meter_sensor.scale).name,
550
- )
566
+ LOGGER.debug("EnergyClamp%s (%s)", nid, name)
567
+ dump_meter(self, zwave)
568
+ dump_multilevelsensor(self, zwave)
569
+ continue
551
570
 
552
571
  if isinstance(zwave, QolsysThermometer):
553
572
  nid = zwave.node_id
554
573
  name = zwave.node_name
555
574
  LOGGER.debug("Thermometer%s (%s)", nid, name)
575
+ dump_meter(self, zwave)
576
+ dump_multilevelsensor(self, zwave)
577
+ continue
556
578
 
557
579
  if isinstance(zwave, QolsysGeneric):
558
580
  zid = zwave.node_id
@@ -561,6 +583,8 @@ class QolsysState(QolsysObservable):
561
583
  LOGGER.debug("Generic%s (%s) - status: %s", zid, name, zwave.node_status)
562
584
  LOGGER.debug("Generic%s (%s) - battery_level: %s", zid, name, zwave.node_battery_level)
563
585
  LOGGER.debug("Generic%s (%s) - battery_level_vale: %s", zid, name, zwave.node_battery_level_value)
586
+ dump_meter(self, zwave)
587
+ dump_multilevelsensor(self, zwave)
564
588
  continue
565
589
 
566
590
  LOGGER.debug("Other Z-Wave devices information")
@@ -1,5 +1,9 @@
1
+ import json
1
2
  import logging
2
3
 
4
+ from qolsys_controller.zwave_service_meter import QolsysZwaveServiceMeter
5
+ from qolsys_controller.zwave_service_multilevelsensor import QolsysZwaveServiceMultilevelSensor
6
+
3
7
  from .enum_zwave import ZwaveDeviceClass
4
8
  from .observable import QolsysObservable
5
9
 
@@ -35,8 +39,14 @@ class QolsysZWaveDevice(QolsysObservable):
35
39
  self._is_device_hidden: str = zwave_dict.get("is_device_hidden", "")
36
40
  self._last_updated_date: str = zwave_dict.get("last_updated_date", "")
37
41
  self._command_class_list: str = zwave_dict.get("command_class_list", "")
38
- self._multisensor_capabilities: str = zwave_dict.get("multisensor_capabilities", "")
39
- self._meter_capabilities: str = zwave_dict.get("meter_capabilities", "")
42
+ self._meter_capabilities: str = ""
43
+ self._multisensor_capabilities: str = ""
44
+
45
+ # Set Meter and MutilevelSensor Services if available
46
+ self._meter_endpoints: list[QolsysZwaveServiceMeter] = []
47
+ self._multilevelsensor_endpoints: list[QolsysZwaveServiceMultilevelSensor] = []
48
+ self.meter_capabilities = zwave_dict.get("meter_capabilities", "")
49
+ self.multisensor_capabilities = zwave_dict.get("multisensor_capabilities", "")
40
50
 
41
51
  def update_raw(self, payload: bytes) -> None:
42
52
  LOGGER.debug("Raw Update (node%s) - payload: %s", self.node_id, payload.hex())
@@ -106,7 +116,7 @@ class QolsysZWaveDevice(QolsysObservable):
106
116
  if "multisensor_capabilities" in data:
107
117
  self.multisensor_capabilities = data.get("multisensor_capabilities", "")
108
118
  if "meter_capabilities" in data:
109
- self._meter_capabilities = data.get("meter_capabilities", "")
119
+ self.meter_capabilities = data.get("meter_capabilities", "")
110
120
 
111
121
  self.end_batch_update()
112
122
 
@@ -151,9 +161,30 @@ class QolsysZWaveDevice(QolsysObservable):
151
161
  @meter_capabilities.setter
152
162
  def meter_capabilities(self, value: str) -> None:
153
163
  if self._meter_capabilities != value:
154
- LOGGER.debug("ZWave%s (%s) - meter_capabilities: %s", self.node_id, self.node_name, value)
164
+ # LOGGER.debug("ZWave%s (%s) - meter_capabilities: %s", self.node_id, self.node_name, value)
155
165
  self._meter_capabilities = value
156
- self.notify()
166
+
167
+ # Update Meter Service
168
+ try:
169
+ meter_services = json.loads(value)
170
+ for endpoint, service in meter_services.items():
171
+ # Check if we already have this meter service
172
+ meter_endpoint = None
173
+ for meter in self._meter_endpoints:
174
+ if meter.endpoint == endpoint:
175
+ meter_endpoint = meter
176
+ meter_endpoint.update_iq2medi(service)
177
+ break
178
+
179
+ # Create new meter service if not found
180
+ if meter_endpoint is None:
181
+ LOGGER.debug("ZWave%s (%s) - Adding new meter endpoint: %s", self.node_id, self.node_name, endpoint)
182
+ meter_endpoint = QolsysZwaveServiceMeter(self, endpoint, service)
183
+ self._meter_endpoints.append(meter_endpoint)
184
+
185
+ except json.JSONDecodeError:
186
+ LOGGER.error("ZWave%s (%s) - Error parsing meter_capabilities:%s", self.node_id, self.node_name, value)
187
+ return
157
188
 
158
189
  @property
159
190
  def multisensor_capabilities(self) -> str:
@@ -162,9 +193,36 @@ class QolsysZWaveDevice(QolsysObservable):
162
193
  @multisensor_capabilities.setter
163
194
  def multisensor_capabilities(self, value: str) -> None:
164
195
  if self._multisensor_capabilities != value:
165
- LOGGER.debug("ZWave%s (%s) - multisensor_capabilities: %s", self.node_id, self.node_name, value)
196
+ # LOGGER.debug("ZWave%s (%s) - multisensor_capabilities: %s", self.node_id, self.node_name, value)
166
197
  self._multisensor_capabilities = value
167
- self.notify()
198
+
199
+ # Update Multilevel Sensor Service
200
+ try:
201
+ sensor_services = json.loads(value)
202
+ for endpoint, service in sensor_services.items():
203
+ # Check if we already have this meter service
204
+ sensor_endpoint = None
205
+ for sensor in self._multilevelsensor_endpoints:
206
+ if sensor.endpoint == endpoint:
207
+ sensor_endpoint = sensor
208
+ sensor_endpoint.update_iq2medi(service)
209
+ break
210
+
211
+ # Create new meter service if not found
212
+ if sensor_endpoint is None:
213
+ LOGGER.debug(
214
+ "ZWave%s (%s) - Adding new multilevelsensor endpoint: %s", self.node_id, self.node_name, endpoint
215
+ )
216
+ LOGGER.debug("create sensor service")
217
+ sensor_endpoint = QolsysZwaveServiceMultilevelSensor(self, endpoint, service)
218
+ self.multilevelsensor_endpoints.append(sensor_endpoint)
219
+ LOGGER.debug(sensor_endpoint.sensors)
220
+
221
+ except json.JSONDecodeError:
222
+ LOGGER.error(
223
+ "ZWave%s (%s) - Error parsing multilevelsensor_capabilities:%s", self.node_id, self.node_name, value
224
+ )
225
+ return
168
226
 
169
227
  @property
170
228
  def node_battery_level_value(self) -> int | None:
@@ -229,6 +287,14 @@ class QolsysZWaveDevice(QolsysObservable):
229
287
  self._partition_id = value
230
288
  self.notify()
231
289
 
290
+ @property
291
+ def meter_endpoints(self) -> list[QolsysZwaveServiceMeter]:
292
+ return self._meter_endpoints
293
+
294
+ @property
295
+ def multilevelsensor_endpoints(self) -> list[QolsysZwaveServiceMultilevelSensor]:
296
+ return self._multilevelsensor_endpoints
297
+
232
298
  @property
233
299
  def generic_device_type(self) -> ZwaveDeviceClass:
234
300
  try:
@@ -239,6 +305,12 @@ class QolsysZWaveDevice(QolsysObservable):
239
305
  def is_battery_enabled(self) -> bool:
240
306
  return self.node_battery_level_value is not None
241
307
 
308
+ def is_service_meter_enabled(self) -> bool:
309
+ return self._meter_endpoints != []
310
+
311
+ def is_service_multilevelsensor_enabled(self) -> bool:
312
+ return self._multilevelsensor_endpoints != []
313
+
242
314
  def to_dict_base(self) -> dict[str, str]:
243
315
  return {
244
316
  "_id": self._id,
@@ -70,7 +70,7 @@ class QolsysDimmer(QolsysZWaveDevice):
70
70
  self.notify()
71
71
 
72
72
  def update_raw(self, payload: bytes) -> None:
73
- LOGGER.debug("Raw Update (node%s) - payload: %s", self.node_id, payload.hex())
73
+ pass
74
74
 
75
75
  def update_dimmer(self, content_values: dict[str, str]) -> None: # noqa: PLR0912
76
76
  # Check if we are updating same none_id
@@ -0,0 +1,15 @@
1
+ import logging
2
+
3
+ from .zwave_device import QolsysZWaveDevice
4
+
5
+ LOGGER = logging.getLogger(__name__)
6
+
7
+
8
+ class QolsysEnergyClamp(QolsysZWaveDevice):
9
+ def __init__(self, zwave_dict: dict[str, str]) -> None:
10
+ super().__init__(zwave_dict)
11
+
12
+ # Energy clamp only enables meter seter in base zwave_device class
13
+
14
+ def update_raw(self, payload: bytes) -> None:
15
+ pass