aiocomfoconnect 0.3.0__tar.gz → 0.3.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiocomfoconnect
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: aiocomfoconnect is an asyncio Python 3 library for communicating with a Zehnder ComfoAir Q350/450/600 ventilation system
5
5
  License-File: LICENSE
6
6
  Author: Michaël Arnauts
@@ -84,6 +84,8 @@ $ python -m aiocomfoconnect get-property --host 192.168.1.213 1 1 8 9 # Unit 0x
84
84
  - `async set_sensor_ventmode_humidity_comfort(mode)`: Set the sensor based ventilation humidity comfort setting. (auto / on / off)
85
85
  - `async get_sensor_ventmode_humidity_protection()`: Get the sensor based ventilation humidity protection setting.
86
86
  - `async set_sensor_ventmode_humidity_protection(mode)`: Set the sensor based ventilation humidity protection setting. (auto / on / off)
87
+ - `async get_property(prop)`: Get the value of a property, e.g. `PROPERTY_RMOT_HEATING_THRESHOLD`.
88
+ - `async get_property_constraints(prop)`: Get the raw value of a numeric property, together with the range and step size the unit accepts.
87
89
 
88
90
  ### Low-level API
89
91
 
@@ -67,6 +67,8 @@ $ python -m aiocomfoconnect get-property --host 192.168.1.213 1 1 8 9 # Unit 0x
67
67
  - `async set_sensor_ventmode_humidity_comfort(mode)`: Set the sensor based ventilation humidity comfort setting. (auto / on / off)
68
68
  - `async get_sensor_ventmode_humidity_protection()`: Get the sensor based ventilation humidity protection setting.
69
69
  - `async set_sensor_ventmode_humidity_protection(mode)`: Set the sensor based ventilation humidity protection setting. (auto / on / off)
70
+ - `async get_property(prop)`: Get the value of a property, e.g. `PROPERTY_RMOT_HEATING_THRESHOLD`.
71
+ - `async get_property_constraints(prop)`: Get the raw value of a numeric property, together with the range and step size the unit accepts.
70
72
 
71
73
  ### Low-level API
72
74
 
@@ -11,6 +11,7 @@ from typing import Literal, Optional
11
11
 
12
12
  from aiocomfoconnect import DEFAULT_NAME, DEFAULT_PIN, DEFAULT_UUID
13
13
  from aiocomfoconnect.comfoconnect import ComfoConnect
14
+ from aiocomfoconnect.const import ProductId
14
15
  from aiocomfoconnect.discovery import discover_bridges
15
16
  from aiocomfoconnect.exceptions import (
16
17
  AioComfoConnectNotConnected,
@@ -24,6 +25,8 @@ from aiocomfoconnect.sensors import SENSORS
24
25
 
25
26
  _LOGGER = logging.getLogger(__name__)
26
27
 
28
+ PRODUCT_NAMES = {value: name.lower() for name, value in vars(ProductId).items() if not name.startswith("_")}
29
+
27
30
 
28
31
  async def main(args):
29
32
  """Main function."""
@@ -57,6 +60,12 @@ async def main(args):
57
60
  elif args.action == "get-property":
58
61
  await run_get_property(args.host, args.uuid, args.node_id, args.unit, args.subunit, args.property_id, args.property_type)
59
62
 
63
+ elif args.action == "set-property":
64
+ await run_set_property(args.host, args.uuid, args.node_id, args.unit, args.subunit, args.property_id, args.property_type, args.value)
65
+
66
+ elif args.action == "list-nodes":
67
+ await run_list_nodes(args.host, args.uuid)
68
+
60
69
  elif args.action == "get-flow-for-speed":
61
70
  await run_get_flow_for_speed(args.host, args.uuid, args.speed)
62
71
 
@@ -341,6 +350,54 @@ async def run_get_property(host: str, uuid: str, node_id: Optional[int], unit: i
341
350
  await comfoconnect.disconnect()
342
351
 
343
352
 
353
+ async def run_set_property(host: str, uuid: str, node_id: Optional[int], unit: int, subunit: int, property_id: int, property_type: int, value: int):
354
+ """Set a property and print the value that is read back."""
355
+ # Discover bridge so we know the UUID
356
+ bridges = await discover_bridges(host)
357
+ if not bridges:
358
+ raise BridgeNotFoundException("No bridge found")
359
+
360
+ # Connect to the bridge
361
+ comfoconnect = ComfoConnect(bridges[0].host, bridges[0].uuid)
362
+ try:
363
+ await comfoconnect.connect(uuid)
364
+ except ComfoConnectNotAllowed:
365
+ print("Could not connect to bridge. Please register first.")
366
+ sys.exit(1)
367
+
368
+ await comfoconnect.set_property_typed(unit, subunit, property_id, value, property_type, node_id=node_id)
369
+ print(await comfoconnect.get_property(Property(unit, subunit, property_id, property_type), node_id))
370
+
371
+ await comfoconnect.disconnect()
372
+
373
+
374
+ async def run_list_nodes(host: str, uuid: str):
375
+ """List the nodes that the bridge sees on the ComfoNet bus."""
376
+ # Discover bridge so we know the UUID
377
+ bridges = await discover_bridges(host)
378
+ if not bridges:
379
+ raise BridgeNotFoundException("No bridge found")
380
+
381
+ # Connect to the bridge
382
+ comfoconnect = ComfoConnect(bridges[0].host, bridges[0].uuid)
383
+ try:
384
+ await comfoconnect.connect(uuid)
385
+ except ComfoConnectNotAllowed:
386
+ print("Could not connect to bridge. Please register first.")
387
+ sys.exit(1)
388
+
389
+ await comfoconnect.cmd_node_request()
390
+ # Nodes arrive as unsolicited notifications, so give the bridge a moment to report them all.
391
+ await asyncio.sleep(2)
392
+
393
+ print("Nodes on the ComfoNet bus:")
394
+ for node in sorted(comfoconnect.nodes.values(), key=lambda n: n.node_id):
395
+ product = PRODUCT_NAMES.get(node.product_id, "unknown")
396
+ print(f"* node {node.node_id}: product {node.product_id} ({product}), zone {node.zone_id}, mode {node.mode}")
397
+
398
+ await comfoconnect.disconnect()
399
+
400
+
344
401
  async def run_get_flow_for_speed(host: str, uuid: str, speed: Literal["away", "low", "medium", "high"]):
345
402
  """Get the configured airflow for the specified speed."""
346
403
  # Discover bridge so we know the UUID
@@ -444,6 +501,20 @@ def main_cli():
444
501
  p_sensor.add_argument("--host", help="Host address of the bridge")
445
502
  p_sensor.add_argument("--uuid", help="UUID of this app", default=DEFAULT_UUID)
446
503
 
504
+ p_set_property = subparsers.add_parser("set-property", help="set a property value")
505
+ p_set_property.add_argument("unit", help="The Unit of the property", type=int)
506
+ p_set_property.add_argument("subunit", help="The Subunit of the property", type=int)
507
+ p_set_property.add_argument("property_id", help="The id of the property", type=int)
508
+ p_set_property.add_argument("property_type", help="The type of the property", type=int)
509
+ p_set_property.add_argument("value", help="The raw value to write", type=int)
510
+ p_set_property.add_argument("--node_id", help="The Node ID to write to (defaults to the discovered ventilation unit)", type=int, default=None)
511
+ p_set_property.add_argument("--host", help="Host address of the bridge")
512
+ p_set_property.add_argument("--uuid", help="UUID of this app", default=DEFAULT_UUID)
513
+
514
+ p_list_nodes = subparsers.add_parser("list-nodes", help="list the nodes on the ComfoNet bus")
515
+ p_list_nodes.add_argument("--host", help="Host address of the bridge")
516
+ p_list_nodes.add_argument("--uuid", help="UUID of this app", default=DEFAULT_UUID)
517
+
447
518
  p_get_flow_speed = subparsers.add_parser("get-flow-for-speed", help="Get m³/h for given speed")
448
519
  p_get_flow_speed.add_argument("speed", help="Fan speed", choices=["low", "medium", "high", "away"])
449
520
  p_get_flow_speed.add_argument("--host", help="Host address of the bridge")
@@ -13,7 +13,7 @@ from typing import Awaitable, Callable, Dict, Iterator, Optional, Set
13
13
  from google.protobuf.message import DecodeError
14
14
  from google.protobuf.message import Message as ProtobufMessage
15
15
 
16
- from .const import VENTILATION_UNIT_PRODUCT_IDS, PdoType
16
+ from .const import VENTILATION_UNIT_PRODUCT_IDS, ProductId, PdoType
17
17
  from .exceptions import (
18
18
  AioComfoConnectNotConnected,
19
19
  AioComfoConnectNotReachable,
@@ -67,6 +67,11 @@ class Node:
67
67
  """Returns True if this node is the ventilation unit that handles RMI commands."""
68
68
  return self.product_id in VENTILATION_UNIT_PRODUCT_IDS
69
69
 
70
+ @property
71
+ def is_comfoclime(self) -> bool:
72
+ """Returns True if this node is a ComfoClime heat pump module."""
73
+ return self.product_id == ProductId.COMFOCLIME
74
+
70
75
 
71
76
  class EventBus:
72
77
  """An event bus for async replies."""
@@ -254,6 +259,18 @@ class Bridge:
254
259
  candidates = [node.node_id for node in self._nodes.values() if node.is_ventilation_unit]
255
260
  return min(candidates) if candidates else None
256
261
 
262
+ @property
263
+ def comfoclime_node_id(self) -> Optional[int]:
264
+ """The node id of the ComfoClime, or None when the bridge hasn't announced one."""
265
+ candidates = [node.node_id for node in self._nodes.values() if node.is_comfoclime]
266
+ return min(candidates) if candidates else None
267
+
268
+ @property
269
+ def comfoclime_zone_id(self) -> Optional[int]:
270
+ """The zone id of the ComfoClime, or None when the bridge hasn't announced one."""
271
+ candidates = [node for node in self._nodes.values() if node.is_comfoclime]
272
+ return min(candidates, key=lambda node: node.node_id).zone_id if candidates else None
273
+
257
274
  async def wait_for_ventilation_node(self, timeout: float = None) -> int:
258
275
  """Wait until the bridge has announced the ventilation unit and return its node id.
259
276
 
@@ -33,15 +33,26 @@ from aiocomfoconnect.exceptions import (
33
33
  AioComfoConnectNotConnected,
34
34
  AioComfoConnectNotReachable,
35
35
  AioComfoConnectTimeout,
36
+ ComfoClimeNotFoundException,
36
37
  ComfoConnectNotAllowed,
37
38
  VentilationUnitNotFoundException,
38
39
  )
39
- from aiocomfoconnect.properties import Property
40
+ from aiocomfoconnect.properties import Property, PropertyConstraints
40
41
  from aiocomfoconnect.sensors import Sensor
41
42
  from aiocomfoconnect.util import bytearray_to_bits, bytestring, encode_pdo_value
42
43
 
43
44
  _LOGGER = logging.getLogger(__name__)
44
45
 
46
+ # The size in bytes and the signedness of the numeric property types.
47
+ NUMERIC_PROPERTY_TYPES = {
48
+ PdoType.TYPE_CN_UINT8: (1, False),
49
+ PdoType.TYPE_CN_UINT16: (2, False),
50
+ PdoType.TYPE_CN_UINT32: (4, False),
51
+ PdoType.TYPE_CN_INT8: (1, True),
52
+ PdoType.TYPE_CN_INT16: (2, True),
53
+ PdoType.TYPE_CN_INT64: (8, True),
54
+ }
55
+
45
56
 
46
57
  class ComfoConnect(Bridge):
47
58
  """Abstraction layer over the ComfoConnect LAN C API."""
@@ -268,6 +279,21 @@ class ComfoConnect(Bridge):
268
279
 
269
280
  return result.message
270
281
 
282
+ async def get_property_constraints(self, prop: Property, node_id: Optional[int] = None) -> PropertyConstraints:
283
+ """Get the raw value of a numeric property, together with the range and step size the unit accepts, in one request."""
284
+ if prop.property_type not in NUMERIC_PROPERTY_TYPES:
285
+ raise ValueError(f"Property type {prop.property_type} has no range")
286
+ size, signed = NUMERIC_PROPERTY_TYPES[prop.property_type]
287
+
288
+ # Request the actual value (0x10), the range (0x20) and the step size (0x40) at once.
289
+ result = await self.cmd_rmi_request(bytes([0x01, prop.unit, prop.subunit, 0x70, prop.property_id]), node_id=node_id)
290
+
291
+ message = bytes(result.message)
292
+ if len(message) != 4 * size:
293
+ raise ValueError(f"Unexpected response length {len(message)} for a value with its range and step size")
294
+ value, minimum, maximum, step = (int.from_bytes(message[i : i + size], byteorder="little", signed=signed) for i in range(0, 4 * size, size))
295
+ return PropertyConstraints(value=value, minimum=minimum, maximum=maximum, step=step)
296
+
271
297
  async def get_multiple_properties(self, unit: int, subunit: int, property_ids: List[int], node_id: Optional[int] = None) -> Any:
272
298
  """Get multiple properties."""
273
299
  result = await self.cmd_rmi_request(bytestring([0x02, unit, subunit, 0x01, 0x10 | len(property_ids), bytes(property_ids)]), node_id=node_id)
@@ -289,6 +315,28 @@ class ComfoConnect(Bridge):
289
315
 
290
316
  return result.message
291
317
 
318
+ def _require_comfoclime_node_id(self) -> int:
319
+ """Return the node id of the ComfoClime, or raise when it isn't on the bus."""
320
+ node_id = self.comfoclime_node_id
321
+ if node_id is None:
322
+ raise ComfoClimeNotFoundException("The bridge has not announced a ComfoClime")
323
+ return node_id
324
+
325
+ async def get_comfoclime_property(self, prop: Property) -> Any:
326
+ """Get a property from the ComfoClime node."""
327
+ return await self.get_property(prop, node_id=self._require_comfoclime_node_id())
328
+
329
+ async def set_comfoclime_property(self, prop: Property, value: int) -> Any:
330
+ """Set a property on the ComfoClime node."""
331
+ return await self.set_property_typed(
332
+ prop.unit,
333
+ prop.subunit,
334
+ prop.property_id,
335
+ value,
336
+ prop.property_type,
337
+ node_id=self._require_comfoclime_node_id(),
338
+ )
339
+
292
340
  def _sensor_callback(self, sensor_id, sensor_value):
293
341
  """Callback function for sensor updates."""
294
342
  if self._sensor_callback_fn is None:
@@ -72,6 +72,11 @@ UNIT_POSTHEATER = 0x27
72
72
  UNIT_COMFOFOND = 0x28
73
73
  UNIT_CO2SENSOR = 0x2B
74
74
  UNIT_SERVICEPRINT = 0x2C
75
+
76
+ # Units that only exist on a ComfoClime node. Unit numbers are reused across product types,
77
+ # so 0x16 / 0x17 mean UNIT_VALVE / UNIT_FAN on a ComfoAir node.
78
+ UNIT_CLIME_TEMPCONFIG = 0x16
79
+ UNIT_CLIME_HEATPUMP = 0x17
75
80
  # UNIT_COOLER(21) = 0x15
76
81
  # UNIT_CC_TEMPERATURESENSOR(22) = 0x16
77
82
  # UNIT_IOSENSOR(21) = 0x15
@@ -245,3 +250,46 @@ class ComfoCoolMode:
245
250
 
246
251
  AUTO = "auto"
247
252
  OFF = "off"
253
+
254
+
255
+ class ComfoClimeSeason:
256
+ """Enum for ComfoClime seasons."""
257
+
258
+ TRANSITION = "transition"
259
+ HEATING = "heating"
260
+ COOLING = "cooling"
261
+
262
+
263
+ class ComfoClimeTemperatureProfile:
264
+ """Enum for ComfoClime temperature profiles."""
265
+
266
+ COMFORT = "comfort"
267
+ POWER = "power"
268
+ ECO = "eco"
269
+
270
+
271
+ class ComfoClimeDeviceMode:
272
+ """Enum for the ComfoClime device mode."""
273
+
274
+ OFF = "off"
275
+ HEATING = "heating"
276
+ COOLING = "cooling"
277
+
278
+
279
+ COMFOCLIME_SEASONS = {
280
+ 0: ComfoClimeSeason.TRANSITION,
281
+ 1: ComfoClimeSeason.HEATING,
282
+ 2: ComfoClimeSeason.COOLING,
283
+ }
284
+
285
+ COMFOCLIME_TEMPERATURE_PROFILES = {
286
+ 0: ComfoClimeTemperatureProfile.COMFORT,
287
+ 1: ComfoClimeTemperatureProfile.POWER,
288
+ 2: ComfoClimeTemperatureProfile.ECO,
289
+ }
290
+
291
+ COMFOCLIME_DEVICE_MODES = {
292
+ 0: ComfoClimeDeviceMode.OFF,
293
+ 1: ComfoClimeDeviceMode.HEATING,
294
+ 2: ComfoClimeDeviceMode.COOLING,
295
+ }
@@ -62,5 +62,9 @@ class VentilationUnitNotFoundException(Exception):
62
62
  """Exception raised when the bridge didn't announce a ventilation unit on the ComfoNet bus."""
63
63
 
64
64
 
65
+ class ComfoClimeNotFoundException(Exception):
66
+ """Exception raised when the bridge didn't announce a ComfoClime on the ComfoNet bus."""
67
+
68
+
65
69
  class UnknownActionException(Exception):
66
70
  """Exception raised when an unknown action is provided."""
@@ -0,0 +1,73 @@
1
+ """ Property definitions """
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from .const import (
8
+ UNIT_CLIME_HEATPUMP,
9
+ UNIT_CLIME_TEMPCONFIG,
10
+ UNIT_NODE,
11
+ UNIT_NODECONFIGURATION,
12
+ UNIT_TEMPHUMCONTROL,
13
+ PdoType,
14
+ )
15
+
16
+
17
+ @dataclass
18
+ class Property:
19
+ """Dataclass for a Property"""
20
+
21
+ unit: int
22
+ subunit: int
23
+ property_id: int
24
+ property_type: int
25
+
26
+
27
+ @dataclass
28
+ class PropertyConstraints:
29
+ """The raw value of a numeric property, with the range and step size the unit accepts."""
30
+
31
+ value: int
32
+ minimum: int
33
+ maximum: int
34
+ step: int
35
+
36
+
37
+ PROPERTY_SERIAL_NUMBER = Property(UNIT_NODE, 0x01, 0x04, PdoType.TYPE_CN_STRING)
38
+ PROPERTY_FIRMWARE_VERSION = Property(UNIT_NODE, 0x01, 0x06, PdoType.TYPE_CN_UINT32)
39
+ PROPERTY_MODEL = Property(UNIT_NODE, 0x01, 0x08, PdoType.TYPE_CN_STRING)
40
+ PROPERTY_ARTICLE = Property(UNIT_NODE, 0x01, 0x0B, PdoType.TYPE_CN_STRING)
41
+ PROPERTY_COUNTRY = Property(UNIT_NODE, 0x01, 0x0D, PdoType.TYPE_CN_STRING)
42
+ PROPERTY_NAME = Property(UNIT_NODE, 0x01, 0x14, PdoType.TYPE_CN_STRING)
43
+
44
+ PROPERTY_MAINTAINER_PASSWORD = Property(UNIT_NODECONFIGURATION, 0x01, 0x03, PdoType.TYPE_CN_STRING)
45
+
46
+ PROPERTY_SENSOR_VENTILATION_TEMP_PASSIVE = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x04, PdoType.TYPE_CN_UINT32)
47
+ PROPERTY_SENSOR_VENTILATION_HUMIDITY_COMFORT = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x06, PdoType.TYPE_CN_UINT32)
48
+ PROPERTY_SENSOR_VENTILATION_HUMIDITY_PROTECTION = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x07, PdoType.TYPE_CN_UINT32)
49
+
50
+ # Temperatures are stored in tenths of a degree.
51
+ PROPERTY_RMOT_HEATING_THRESHOLD = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x02, PdoType.TYPE_CN_INT16)
52
+ PROPERTY_RMOT_COOLING_THRESHOLD = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x03, PdoType.TYPE_CN_INT16)
53
+ PROPERTY_PROFILE_TEMPERATURE_WARM = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x0A, PdoType.TYPE_CN_INT16)
54
+ PROPERTY_PROFILE_TEMPERATURE_NORMAL = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x0B, PdoType.TYPE_CN_INT16)
55
+ PROPERTY_PROFILE_TEMPERATURE_COOL = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x0C, PdoType.TYPE_CN_INT16)
56
+
57
+ # 0=adaptive (the target follows the RMOT), 1=fixed (the target is the temperature of the active profile).
58
+ PROPERTY_TEMPERATURE_PROFILE_MODE = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x08, PdoType.TYPE_CN_UINT8)
59
+
60
+ # ComfoClime properties. These live on the ComfoClime node, not on the ventilation unit.
61
+ PROPERTY_CLIME_AUTO_SEASON = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x02, PdoType.TYPE_CN_UINT8)
62
+ PROPERTY_CLIME_SEASON = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x03, PdoType.TYPE_CN_UINT8)
63
+ PROPERTY_CLIME_HEATING_KNEE_POINT = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x04, PdoType.TYPE_CN_INT16)
64
+ PROPERTY_CLIME_COOLING_KNEE_POINT = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x05, PdoType.TYPE_CN_INT16)
65
+ PROPERTY_CLIME_HEATING_COMFORT_TEMP = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x09, PdoType.TYPE_CN_INT16)
66
+ PROPERTY_CLIME_COOLING_COMFORT_TEMP = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x0A, PdoType.TYPE_CN_INT16)
67
+ PROPERTY_CLIME_COOLING_TEMP_LIMIT = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x0B, PdoType.TYPE_CN_INT16)
68
+ PROPERTY_CLIME_HEATING_DELTA = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x0C, PdoType.TYPE_CN_INT16)
69
+ PROPERTY_CLIME_MANUAL_TARGET_TEMP = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x0D, PdoType.TYPE_CN_INT16)
70
+ PROPERTY_CLIME_TEMPERATURE_PROFILE = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x1D, PdoType.TYPE_CN_UINT8)
71
+ PROPERTY_CLIME_HEATPUMP_STANDBY = Property(UNIT_CLIME_TEMPCONFIG, 0x01, 0x15, PdoType.TYPE_CN_UINT8)
72
+ PROPERTY_CLIME_HEATPUMP_MAX_TEMP = Property(UNIT_CLIME_HEATPUMP, 0x01, 0x03, PdoType.TYPE_CN_INT16)
73
+ PROPERTY_CLIME_HEATPUMP_MIN_TEMP = Property(UNIT_CLIME_HEATPUMP, 0x01, 0x04, PdoType.TYPE_CN_INT16)
@@ -78,6 +78,20 @@ SENSOR_UNIT_TEMPERATURE = 208
78
78
  SENSOR_COMFOCOOL_STATE = 784
79
79
  SENSOR_COMFOCOOL_CONDENSOR_TEMP = 802
80
80
 
81
+ # ComfoClime sensors. These are only reported when a ComfoClime is present on the ComfoNet bus.
82
+ SENSOR_COMFOCLIME_TPMA_TEMPERATURE = 4145
83
+ SENSOR_COMFOCLIME_TARGET_TEMPERATURE = 4148
84
+ SENSOR_COMFOCLIME_DEVICE_MODE = 4149
85
+ SENSOR_COMFOCLIME_COMFORT_TEMPERATURE = 4151
86
+ SENSOR_COMFOCLIME_INDOOR_TEMPERATURE = 4154
87
+ SENSOR_COMFOCLIME_TEMPERATURE_SUPPLY = 4193
88
+ SENSOR_COMFOCLIME_TEMPERATURE_EXHAUST = 4194
89
+ SENSOR_COMFOCLIME_TEMPERATURE_SUPPLY_COIL = 4195
90
+ SENSOR_COMFOCLIME_TEMPERATURE_EXHAUST_COIL = 4196
91
+ SENSOR_COMFOCLIME_TEMPERATURE_COMPRESSOR = 4197
92
+ SENSOR_COMFOCLIME_HEATPUMP_DUTY = 4198
93
+ SENSOR_COMFOCLIME_POWER_USAGE = 4201
94
+
81
95
  UNIT_WATT = "W"
82
96
  UNIT_KWH = "kWh"
83
97
  UNIT_VOLT = "V"
@@ -133,7 +147,7 @@ SENSORS: Dict[int, Sensor] = {
133
147
  SENSOR_PREHEATER_POWER: Sensor("Preheater Power Usage", UNIT_WATT, 146, PdoType.TYPE_CN_UINT16),
134
148
  SENSOR_RF_PAIRING_MODE: Sensor("RF Pairing Mode", None, 176, PdoType.TYPE_CN_UINT8),
135
149
  SENSOR_DAYS_TO_REPLACE_FILTER: Sensor("Days remaining to replace the filter", None, 192, PdoType.TYPE_CN_UINT16),
136
- SENSOR_UNIT_TEMPERATURE: Sensor("Device Temperature Unit", None, 208, PdoType.TYPE_CN_UINT8, lambda x: "celcius" if x == 0 else "farenheit"),
150
+ SENSOR_UNIT_TEMPERATURE: Sensor("Device Temperature Unit", None, 208, PdoType.TYPE_CN_UINT8, lambda x: {0: "celsius", 1: "fahrenheit"}.get(x, x)),
137
151
  SENSOR_RMOT: Sensor("Running Mean Outdoor Temperature (RMOT)", UNIT_CELCIUS, 209, PdoType.TYPE_CN_INT16, lambda x: x / 10),
138
152
  SENSOR_SEASON_HEATING_ACTIVE: Sensor("Heating Season is active", None, 210, PdoType.TYPE_CN_BOOL, bool),
139
153
  SENSOR_SEASON_COOLING_ACTIVE: Sensor("Cooling Season is active", None, 211, PdoType.TYPE_CN_BOOL, bool),
@@ -147,7 +161,7 @@ SENSORS: Dict[int, Sensor] = {
147
161
  219: Sensor("sensor_219", None, 219, PdoType.TYPE_CN_UINT16),
148
162
  220: Sensor("Outdoor Air Temperature (?)", None, 220, PdoType.TYPE_CN_INT16, lambda x: x / 10),
149
163
  SENSOR_TEMPERATURE_SUPPLY: Sensor("Supply Air Temperature", UNIT_CELCIUS, 221, PdoType.TYPE_CN_INT16, lambda x: x / 10),
150
- SENSOR_UNIT_AIRFLOW: Sensor("Device Airflow Unit", None, 224, PdoType.TYPE_CN_UINT8, lambda x: "m3ph" if x == 3 else "lps"),
164
+ SENSOR_UNIT_AIRFLOW: Sensor("Device Airflow Unit", None, 224, PdoType.TYPE_CN_UINT8, lambda x: {1: "kgph", 2: "lps", 3: "m3ph"}.get(x, x)),
151
165
  SENSOR_COMFORTCONTROL_MODE: Sensor("Sensor based ventilation mode", None, 225, PdoType.TYPE_CN_UINT8),
152
166
  SENSOR_FAN_SPEED_MODE_MODULATED: Sensor("Fan Speed (modulated)", None, 226, PdoType.TYPE_CN_UINT16),
153
167
  SENSOR_BYPASS_STATE: Sensor("Bypass State", UNIT_PERCENT, 227, PdoType.TYPE_CN_UINT8),
@@ -186,4 +200,23 @@ SENSORS: Dict[int, Sensor] = {
186
200
  SENSOR_COMFOCOOL_STATE: Sensor("ComfoCool State", None, 784, PdoType.TYPE_CN_UINT8),
187
201
  785: Sensor("sensor_785", None, 785, PdoType.TYPE_CN_BOOL),
188
202
  SENSOR_COMFOCOOL_CONDENSOR_TEMP: Sensor("ComfoCool Condensor Temperature", None, 802, PdoType.TYPE_CN_INT16, lambda x: x / 10),
203
+ SENSOR_COMFOCLIME_TPMA_TEMPERATURE: Sensor("ComfoClime TPMA Temperature", UNIT_CELCIUS, 4145, PdoType.TYPE_CN_INT16, lambda x: x / 10),
204
+ SENSOR_COMFOCLIME_TARGET_TEMPERATURE: Sensor("ComfoClime Target Temperature", UNIT_CELCIUS, 4148, PdoType.TYPE_CN_INT16, lambda x: x / 10),
205
+ SENSOR_COMFOCLIME_DEVICE_MODE: Sensor("ComfoClime Device Mode", None, 4149, PdoType.TYPE_CN_UINT8),
206
+ SENSOR_COMFOCLIME_COMFORT_TEMPERATURE: Sensor("ComfoClime Comfort Temperature", UNIT_CELCIUS, 4151, PdoType.TYPE_CN_INT16, lambda x: x / 10),
207
+ SENSOR_COMFOCLIME_INDOOR_TEMPERATURE: Sensor("ComfoClime Indoor Temperature", UNIT_CELCIUS, 4154, PdoType.TYPE_CN_INT16, lambda x: x / 10),
208
+ SENSOR_COMFOCLIME_TEMPERATURE_SUPPLY: Sensor("ComfoClime Supply Air Temperature", UNIT_CELCIUS, 4193, PdoType.TYPE_CN_INT16, lambda x: x / 10),
209
+ SENSOR_COMFOCLIME_TEMPERATURE_EXHAUST: Sensor("ComfoClime Exhaust Air Temperature", UNIT_CELCIUS, 4194, PdoType.TYPE_CN_INT16, lambda x: x / 10),
210
+ SENSOR_COMFOCLIME_TEMPERATURE_SUPPLY_COIL: Sensor("ComfoClime Supply Coil Temperature", UNIT_CELCIUS, 4195, PdoType.TYPE_CN_INT16, lambda x: x / 10),
211
+ SENSOR_COMFOCLIME_TEMPERATURE_EXHAUST_COIL: Sensor("ComfoClime Exhaust Coil Temperature", UNIT_CELCIUS, 4196, PdoType.TYPE_CN_INT16, lambda x: x / 10),
212
+ SENSOR_COMFOCLIME_TEMPERATURE_COMPRESSOR: Sensor("ComfoClime Compressor Temperature", UNIT_CELCIUS, 4197, PdoType.TYPE_CN_INT16, lambda x: x / 10),
213
+ SENSOR_COMFOCLIME_HEATPUMP_DUTY: Sensor("ComfoClime Heat Pump Duty", UNIT_PERCENT, 4198, PdoType.TYPE_CN_UINT8),
214
+ SENSOR_COMFOCLIME_POWER_USAGE: Sensor("ComfoClime Power Usage", UNIT_WATT, 4201, PdoType.TYPE_CN_UINT16),
215
+ 4202: Sensor("sensor_4202", None, 4202, PdoType.TYPE_CN_UINT16),
216
+ 4203: Sensor("sensor_4203", None, 4203, PdoType.TYPE_CN_UINT16),
217
+ 4204: Sensor("sensor_4204", None, 4204, PdoType.TYPE_CN_UINT16),
218
+ 4205: Sensor("sensor_4205", None, 4205, PdoType.TYPE_CN_UINT16),
219
+ 4206: Sensor("sensor_4206", None, 4206, PdoType.TYPE_CN_UINT16),
220
+ 4207: Sensor("sensor_4207", None, 4207, PdoType.TYPE_CN_UINT16),
221
+ 4208: Sensor("sensor_4208", None, 4208, PdoType.TYPE_CN_UINT16),
189
222
  }
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aiocomfoconnect"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "aiocomfoconnect is an asyncio Python 3 library for communicating with a Zehnder ComfoAir Q350/450/600 ventilation system"
5
5
  authors = ["Michaël Arnauts <michael.arnauts@gmail.com>"]
6
6
  readme = "README.md"
@@ -1,31 +0,0 @@
1
- """ Property definitions """
2
-
3
- from __future__ import annotations
4
-
5
- from dataclasses import dataclass
6
-
7
- from .const import UNIT_NODE, UNIT_NODECONFIGURATION, UNIT_TEMPHUMCONTROL, PdoType
8
-
9
-
10
- @dataclass
11
- class Property:
12
- """Dataclass for a Property"""
13
-
14
- unit: int
15
- subunit: int
16
- property_id: int
17
- property_type: int
18
-
19
-
20
- PROPERTY_SERIAL_NUMBER = Property(UNIT_NODE, 0x01, 0x04, PdoType.TYPE_CN_STRING)
21
- PROPERTY_FIRMWARE_VERSION = Property(UNIT_NODE, 0x01, 0x06, PdoType.TYPE_CN_UINT32)
22
- PROPERTY_MODEL = Property(UNIT_NODE, 0x01, 0x08, PdoType.TYPE_CN_STRING)
23
- PROPERTY_ARTICLE = Property(UNIT_NODE, 0x01, 0x0B, PdoType.TYPE_CN_STRING)
24
- PROPERTY_COUNTRY = Property(UNIT_NODE, 0x01, 0x0D, PdoType.TYPE_CN_STRING)
25
- PROPERTY_NAME = Property(UNIT_NODE, 0x01, 0x14, PdoType.TYPE_CN_STRING)
26
-
27
- PROPERTY_MAINTAINER_PASSWORD = Property(UNIT_NODECONFIGURATION, 0x01, 0x03, PdoType.TYPE_CN_STRING)
28
-
29
- PROPERTY_SENSOR_VENTILATION_TEMP_PASSIVE = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x04, PdoType.TYPE_CN_UINT32)
30
- PROPERTY_SENSOR_VENTILATION_HUMIDITY_COMFORT = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x06, PdoType.TYPE_CN_UINT32)
31
- PROPERTY_SENSOR_VENTILATION_HUMIDITY_PROTECTION = Property(UNIT_TEMPHUMCONTROL, 0x01, 0x07, PdoType.TYPE_CN_UINT32)
File without changes