ramses-rf 0.52.5__py3-none-any.whl → 0.53.1__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.
ramses_rf/gateway.py CHANGED
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import asyncio
13
13
  import logging
14
+ from collections.abc import Awaitable, Callable
14
15
  from types import SimpleNamespace
15
16
  from typing import TYPE_CHECKING, Any
16
17
 
@@ -83,7 +84,12 @@ _LOGGER = logging.getLogger(__name__)
83
84
 
84
85
 
85
86
  class Gateway(Engine):
86
- """The gateway class."""
87
+ """The gateway class.
88
+
89
+ This class serves as the primary interface for the RAMSES RF network. It manages
90
+ the serial connection (via ``Engine``), device discovery, schema maintenance,
91
+ and message dispatching.
92
+ """
87
93
 
88
94
  def __init__(
89
95
  self,
@@ -94,8 +100,30 @@ class Gateway(Engine):
94
100
  block_list: DeviceListT | None = None,
95
101
  known_list: DeviceListT | None = None,
96
102
  loop: asyncio.AbstractEventLoop | None = None,
103
+ transport_constructor: Callable[..., Awaitable[RamsesTransportT]] | None = None,
97
104
  **kwargs: Any,
98
105
  ) -> None:
106
+ """Initialize the Gateway instance.
107
+
108
+ :param port_name: The serial port name (e.g., '/dev/ttyUSB0') or None if using a file.
109
+ :type port_name: str | None
110
+ :param input_file: Path to a packet log file for playback/parsing, defaults to None.
111
+ :type input_file: str | None, optional
112
+ :param port_config: Configuration dictionary for the serial port, defaults to None.
113
+ :type port_config: PortConfigT | None, optional
114
+ :param packet_log: Configuration for packet logging, defaults to None.
115
+ :type packet_log: PktLogConfigT | None, optional
116
+ :param block_list: A list of device IDs to block/ignore, defaults to None.
117
+ :type block_list: DeviceListT | None, optional
118
+ :param known_list: A list of known device IDs and their traits, defaults to None.
119
+ :type known_list: DeviceListT | None, optional
120
+ :param loop: The asyncio event loop to use, defaults to None.
121
+ :type loop: asyncio.AbstractEventLoop | None, optional
122
+ :param transport_constructor: A factory for creating the transport layer, defaults to None.
123
+ :type transport_constructor: Callable[..., Awaitable[RamsesTransportT]] | None, optional
124
+ :param kwargs: Additional configuration parameters passed to the engine and schema.
125
+ :type kwargs: Any
126
+ """
99
127
  if kwargs.pop("debug_mode", None):
100
128
  _LOGGER.setLevel(logging.DEBUG)
101
129
 
@@ -110,6 +138,7 @@ class Gateway(Engine):
110
138
  block_list=block_list,
111
139
  known_list=known_list,
112
140
  loop=loop,
141
+ transport_constructor=transport_constructor,
113
142
  **SCH_ENGINE_CONFIG(config),
114
143
  )
115
144
 
@@ -132,13 +161,23 @@ class Gateway(Engine):
132
161
  self.msg_db: MessageIndex | None = None
133
162
 
134
163
  def __repr__(self) -> str:
164
+ """Return a string representation of the Gateway.
165
+
166
+ :returns: A string describing the gateway's input source (port or file).
167
+ :rtype: str
168
+ """
135
169
  if not self.ser_name:
136
170
  return f"Gateway(input_file={self._input_file})"
137
171
  return f"Gateway(port_name={self.ser_name}, port_config={self._port_config})"
138
172
 
139
173
  @property
140
174
  def hgi(self) -> HgiGateway | None:
141
- """Return the active HGI80-compatible gateway device, if known."""
175
+ """Return the active HGI80-compatible gateway device, if known.
176
+
177
+ :returns: The gateway device instance or None if the transport is not set up
178
+ or the HGI ID is not found.
179
+ :rtype: HgiGateway | None
180
+ """
142
181
  if not self._transport:
143
182
  return None
144
183
  if device_id := self._transport.get_extra_info(SZ_ACTIVE_HGI):
@@ -152,10 +191,21 @@ class Gateway(Engine):
152
191
  start_discovery: bool = True,
153
192
  cached_packets: dict[str, str] | None = None,
154
193
  ) -> None:
155
- """Start the Gateway and Initiate discovery as required."""
194
+ """Start the Gateway and Initiate discovery as required.
195
+
196
+ This method initializes packet logging, the SQLite index, loads the schema,
197
+ and optionally restores state from cached packets before starting the transport.
198
+
199
+ :param start_discovery: Whether to initiate the discovery process after start, defaults to True.
200
+ :type start_discovery: bool, optional
201
+ :param cached_packets: A dictionary of packet strings used to restore state, defaults to None.
202
+ :type cached_packets: dict[str, str] | None, optional
203
+ :returns: None
204
+ :rtype: None
205
+ """
156
206
 
157
207
  def initiate_discovery(dev_list: list[Device], sys_list: list[Evohome]) -> None:
158
- _LOGGER.debug("ENGINE: Initiating/enabling discovery...")
208
+ _LOGGER.debug("Engine: Initiating/enabling discovery...")
159
209
 
160
210
  # [d._start_discovery_poller() for d in devs]
161
211
  for device in dev_list:
@@ -174,9 +224,10 @@ class Gateway(Engine):
174
224
  )
175
225
 
176
226
  # initialize SQLite index, set in _tx/Engine
177
- if self._sqlite_index: # TODO(eb): default to ON in Q4 2025
227
+ if self._sqlite_index: # TODO(eb): default to True in Q1 2026
178
228
  _LOGGER.info("Ramses RF starts SQLite MessageIndex")
179
- self.create_sqlite_message_index() # if activated in ramses_cc > Engine
229
+ # if activated in ramses_cc > Engine or set in tests
230
+ self.create_sqlite_message_index()
180
231
 
181
232
  # temporarily turn on discovery, remember original state
182
233
  self.config.disable_discovery, disable_discovery = (
@@ -201,10 +252,21 @@ class Gateway(Engine):
201
252
  initiate_discovery(self.devices, self.systems)
202
253
 
203
254
  def create_sqlite_message_index(self) -> None:
255
+ """Initialize the SQLite MessageIndex.
256
+
257
+ :returns: None
258
+ :rtype: None
259
+ """
204
260
  self.msg_db = MessageIndex() # start the index
205
261
 
206
262
  async def stop(self) -> None:
207
- """Stop the Gateway and tidy up."""
263
+ """Stop the Gateway and tidy up.
264
+
265
+ Stops the message database and the underlying engine/transport.
266
+
267
+ :returns: None
268
+ :rtype: None
269
+ """
208
270
 
209
271
  if self.msg_db:
210
272
  self.msg_db.stop()
@@ -214,6 +276,12 @@ class Gateway(Engine):
214
276
  """Pause the (unpaused) gateway (disables sending/discovery).
215
277
 
216
278
  There is the option to save other objects, as `args`.
279
+
280
+ :param args: Additional objects/state to save during the pause.
281
+ :type args: Any
282
+ :returns: None
283
+ :rtype: None
284
+ :raises RuntimeError: If the engine fails to pause.
217
285
  """
218
286
  _LOGGER.debug("Gateway: Pausing engine...")
219
287
 
@@ -229,6 +297,9 @@ class Gateway(Engine):
229
297
  """Resume the (paused) gateway (enables sending/discovery, if applicable).
230
298
 
231
299
  Will restore other objects, as `args`.
300
+
301
+ :returns: A tuple of arguments saved during the pause.
302
+ :rtype: tuple[Any]
232
303
  """
233
304
  args: tuple[Any]
234
305
 
@@ -241,7 +312,13 @@ class Gateway(Engine):
241
312
  def get_state(
242
313
  self, include_expired: bool = False
243
314
  ) -> tuple[dict[str, Any], dict[str, str]]:
244
- """Return the current schema & state (may include expired packets)."""
315
+ """Return the current schema & state (may include expired packets).
316
+
317
+ :param include_expired: If True, include expired packets in the state, defaults to False.
318
+ :type include_expired: bool, optional
319
+ :returns: A tuple containing the schema dictionary and the packet log dictionary.
320
+ :rtype: tuple[dict[str, Any], dict[str, str]]
321
+ """
245
322
 
246
323
  self._pause()
247
324
 
@@ -287,10 +364,21 @@ class Gateway(Engine):
287
364
  async def _restore_cached_packets(
288
365
  self, packets: dict[str, str], _clear_state: bool = False
289
366
  ) -> None:
290
- """Restore cached packets (may include expired packets)."""
367
+ """Restore cached packets (may include expired packets).
368
+
369
+ This process uses a temporary transport to replay the packet history
370
+ into the message handler.
371
+
372
+ :param packets: A dictionary of packet strings.
373
+ :type packets: dict[str, str]
374
+ :param _clear_state: If True, reset internal state before restoration (for testing), defaults to False.
375
+ :type _clear_state: bool, optional
376
+ :returns: None
377
+ :rtype: None
378
+ """
291
379
 
292
380
  def clear_state() -> None:
293
- _LOGGER.info("GATEWAY: Clearing existing schema/state...")
381
+ _LOGGER.info("Gateway: Clearing existing schema/state...")
294
382
 
295
383
  # self._schema = {}
296
384
 
@@ -303,7 +391,7 @@ class Gateway(Engine):
303
391
 
304
392
  tmp_transport: RamsesTransportT # mypy hint
305
393
 
306
- _LOGGER.debug("GATEWAY: Restoring a cached packet log...")
394
+ _LOGGER.debug("Gateway: Restoring a cached packet log...")
307
395
  self._pause()
308
396
 
309
397
  if _clear_state: # only intended for test suite use
@@ -339,11 +427,18 @@ class Gateway(Engine):
339
427
 
340
428
  await tmp_transport.get_extra_info(SZ_READER_TASK)
341
429
 
342
- _LOGGER.debug("GATEWAY: Restored, resuming")
430
+ _LOGGER.debug("Gateway: Restored, resuming")
343
431
  self._resume()
344
432
 
345
433
  def _add_device(self, dev: Device) -> None: # TODO: also: _add_system()
346
- """Add a device to the gateway (called by devices during instantiation)."""
434
+ """Add a device to the gateway (called by devices during instantiation).
435
+
436
+ :param dev: The device instance to add.
437
+ :type dev: Device
438
+ :returns: None
439
+ :rtype: None
440
+ :raises LookupError: If the device already exists in the gateway.
441
+ """
347
442
 
348
443
  if dev.id in self.device_by_id:
349
444
  raise LookupError(f"Device already exists: {dev.id}")
@@ -366,17 +461,17 @@ class Gateway(Engine):
366
461
  passes a message for it to handle. All devices have traits, but only
367
462
  controllers (CTL, UFC) have a schema.
368
463
 
369
- :param device_id: The unique identifier for the device (e.g., '01:123456')
464
+ :param device_id: The unique identifier for the device (e.g., '01:123456').
370
465
  :type device_id: DeviceIdT
371
- :param msg: An optional initial message for the device to process, defaults to None
372
- :type msg: Message | None
373
- :param parent: The parent entity of this device, if any, defaults to None
374
- :type parent: Parent | None
375
- :param child_id: The specific ID of the child component if applicable, defaults to None
376
- :type child_id: str | None
377
- :param is_sensor: Indicates if this device should be treated as a sensor, defaults to None
378
- :type is_sensor: bool | None
379
- :return: The existing or newly created device instance
466
+ :param msg: An optional initial message for the device to process, defaults to None.
467
+ :type msg: Message | None, optional
468
+ :param parent: The parent entity of this device, if any, defaults to None.
469
+ :type parent: Parent | None, optional
470
+ :param child_id: The specific ID of the child component if applicable, defaults to None.
471
+ :type child_id: str | None, optional
472
+ :param is_sensor: Indicates if this device should be treated as a sensor, defaults to None.
473
+ :type is_sensor: bool | None, optional
474
+ :returns: The existing or newly created device instance.
380
475
  :rtype: Device
381
476
  :raises LookupError: If the device ID is blocked or not in the allowed known_list.
382
477
  """
@@ -446,7 +541,21 @@ class Gateway(Engine):
446
541
  device_id: DeviceIdT,
447
542
  create_device: bool = False,
448
543
  ) -> Device | Fakeable:
449
- """Create a faked device."""
544
+ """Create a faked device.
545
+
546
+ Converts an existing device to a fake device, or creates a new fake device
547
+ if it satisfies strict criteria (valid ID, presence in known_list).
548
+
549
+ :param device_id: The ID of the device to fake.
550
+ :type device_id: DeviceIdT
551
+ :param create_device: If True, allow creation of a new device if it doesn't exist, defaults to False.
552
+ :type create_device: bool, optional
553
+ :returns: The faked device instance.
554
+ :rtype: Device | Fakeable
555
+ :raises TypeError: If the device ID is invalid or the device is not fakeable.
556
+ :raises LookupError: If the device does not exist and create_device is False,
557
+ or if create_device is True but the ID is not in known_list.
558
+ """
450
559
 
451
560
  if not is_valid_dev_id(device_id):
452
561
  raise TypeError(f"The device id is not valid: {device_id}")
@@ -464,7 +573,11 @@ class Gateway(Engine):
464
573
 
465
574
  @property
466
575
  def tcs(self) -> Evohome | None:
467
- """Return the primary TCS, if any."""
576
+ """Return the primary Temperature Control System (TCS), if any.
577
+
578
+ :returns: The primary Evohome system or None.
579
+ :rtype: Evohome | None
580
+ """
468
581
 
469
582
  if self._tcs is None and self.systems:
470
583
  self._tcs = self.systems[0]
@@ -477,6 +590,9 @@ class Gateway(Engine):
477
590
  Unlike orphans, which are always instantiated when a schema is loaded, these
478
591
  devices may/may not exist. However, if they are ever instantiated, they should
479
592
  be given these traits.
593
+
594
+ :returns: A dictionary where keys are device IDs and values are their traits.
595
+ :rtype: DeviceListT
480
596
  """
481
597
 
482
598
  result = self._include # could be devices here, not (yet) in gwy.devices
@@ -491,6 +607,11 @@ class Gateway(Engine):
491
607
 
492
608
  @property
493
609
  def system_by_id(self) -> dict[DeviceIdT, Evohome]:
610
+ """Return a mapping of device IDs to their associated Evohome systems.
611
+
612
+ :returns: A dictionary mapping DeviceId to Evohome instances.
613
+ :rtype: dict[DeviceIdT, Evohome]
614
+ """
494
615
  return {
495
616
  d.id: d.tcs
496
617
  for d in self.devices
@@ -499,6 +620,11 @@ class Gateway(Engine):
499
620
 
500
621
  @property
501
622
  def systems(self) -> list[Evohome]:
623
+ """Return a list of all identified Evohome systems.
624
+
625
+ :returns: A list of Evohome system instances.
626
+ :rtype: list[Evohome]
627
+ """
502
628
  return list(self.system_by_id.values())
503
629
 
504
630
  @property
@@ -510,6 +636,9 @@ class Gateway(Engine):
510
636
  - schema (everything else)
511
637
  - known_list
512
638
  - block_list
639
+
640
+ :returns: A dictionary representing the current internal configuration state.
641
+ :rtype: dict[str, Any]
513
642
  """
514
643
 
515
644
  return {
@@ -531,6 +660,9 @@ class Gateway(Engine):
531
660
  Orphans are devices that 'exist' but don't yet have a place in the schema
532
661
  hierarchy (if ever): therefore, they are instantiated when the schema is loaded,
533
662
  just like the other devices in the schema.
663
+
664
+ :returns: A dictionary representing the entire system schema structure.
665
+ :rtype: dict[str, Any]
534
666
  """
535
667
 
536
668
  schema: dict[str, Any] = {SZ_MAIN_TCS: self.tcs.ctl.id if self.tcs else None}
@@ -558,10 +690,20 @@ class Gateway(Engine):
558
690
 
559
691
  @property
560
692
  def params(self) -> dict[str, Any]:
693
+ """Return the parameters for all devices.
694
+
695
+ :returns: A dictionary containing parameters for every device in the gateway.
696
+ :rtype: dict[str, Any]
697
+ """
561
698
  return {SZ_DEVICES: {d.id: d.params for d in sorted(self.devices)}}
562
699
 
563
700
  @property
564
701
  def status(self) -> dict[str, Any]:
702
+ """Return the status for all devices and the transport rate.
703
+
704
+ :returns: A dictionary containing device statuses and transmission rate.
705
+ :rtype: dict[str, Any]
706
+ """
565
707
  tx_rate = self._transport.get_extra_info("tx_rate") if self._transport else None
566
708
  return {
567
709
  SZ_DEVICES: {d.id: d.status for d in sorted(self.devices)},
@@ -569,7 +711,15 @@ class Gateway(Engine):
569
711
  }
570
712
 
571
713
  def _msg_handler(self, msg: Message) -> None:
572
- """A callback to handle messages from the protocol stack."""
714
+ """A callback to handle messages from the protocol stack.
715
+
716
+ Handles message reassembly (fragmentation) and dispatches the message for processing.
717
+
718
+ :param msg: The incoming message to handle.
719
+ :type msg: Message
720
+ :returns: None
721
+ :rtype: None
722
+ """
573
723
 
574
724
  super()._msg_handler(msg)
575
725
 
@@ -597,9 +747,20 @@ class Gateway(Engine):
597
747
  ) -> asyncio.Task[Packet]:
598
748
  """Wrapper to schedule an async_send_cmd() and return the Task.
599
749
 
600
- num_repeats: 0 = send once, 1 = send twice, etc.
601
- gap_duration: the gap between repeats (in seconds)
602
- priority: the priority of the command
750
+ :param cmd: The command object to send.
751
+ :type cmd: Command
752
+ :param gap_duration: The gap between repeats (in seconds), defaults to DEFAULT_GAP_DURATION.
753
+ :type gap_duration: float, optional
754
+ :param num_repeats: Number of times to repeat the command (0 = once, 1 = twice, etc.), defaults to DEFAULT_NUM_REPEATS.
755
+ :type num_repeats: int, optional
756
+ :param priority: The priority of the command, defaults to Priority.DEFAULT.
757
+ :type priority: Priority, optional
758
+ :param timeout: Time to wait for a send to complete, defaults to DEFAULT_SEND_TIMEOUT.
759
+ :type timeout: float, optional
760
+ :param wait_for_reply: Whether to wait for a reply packet, defaults to DEFAULT_WAIT_FOR_REPLY.
761
+ :type wait_for_reply: bool | None, optional
762
+ :returns: The asyncio Task wrapping the send operation.
763
+ :rtype: asyncio.Task[Packet]
603
764
  """
604
765
 
605
766
  coro = self.async_send_cmd(
@@ -632,6 +793,22 @@ class Gateway(Engine):
632
793
  If wait_for_reply is True (*and* the Command has a rx_header), return the
633
794
  reply Packet. Otherwise, simply return the echo Packet.
634
795
 
796
+ :param cmd: The command object to send.
797
+ :type cmd: Command
798
+ :param gap_duration: The gap between repeats (in seconds), defaults to DEFAULT_GAP_DURATION.
799
+ :type gap_duration: float, optional
800
+ :param num_repeats: Number of times to repeat the command, defaults to DEFAULT_NUM_REPEATS.
801
+ :type num_repeats: int, optional
802
+ :param priority: The priority of the command, defaults to Priority.DEFAULT.
803
+ :type priority: Priority, optional
804
+ :param max_retries: Maximum number of retries if sending fails, defaults to DEFAULT_MAX_RETRIES.
805
+ :type max_retries: int, optional
806
+ :param timeout: Time to wait for the command to send, defaults to DEFAULT_SEND_TIMEOUT.
807
+ :type timeout: float, optional
808
+ :param wait_for_reply: Whether to wait for a reply packet, defaults to DEFAULT_WAIT_FOR_REPLY.
809
+ :type wait_for_reply: bool | None, optional
810
+ :returns: The echo packet or reply packet depending on wait_for_reply.
811
+ :rtype: Packet
635
812
  :raises ProtocolSendFailed: If the command was sent but no reply/echo was received.
636
813
  :raises ProtocolError: If the system failed to attempt the transmission.
637
814
  """
ramses_rf/schemas.py CHANGED
@@ -237,6 +237,7 @@ SCH_GLOBAL_SCHEMAS_DICT = { # System schemas - can be 0-many Heat/HVAC schemas
237
237
  vol.Optional(SCH_DEVICE_ID_ANY): SCH_VCS, # must be after SCH_DEVICE_ID_CTL
238
238
  vol.Optional(SZ_ORPHANS_HEAT): vol.All([SCH_DEVICE_ID_ANY], vol.Unique()),
239
239
  vol.Optional(SZ_ORPHANS_HVAC): vol.All([SCH_DEVICE_ID_ANY], vol.Unique()),
240
+ vol.Optional("transport_constructor"): vol.Any(callable, None),
240
241
  }
241
242
  SCH_GLOBAL_SCHEMAS = vol.Schema(SCH_GLOBAL_SCHEMAS_DICT, extra=vol.PREVENT_EXTRA)
242
243
 
ramses_rf/system/zones.py CHANGED
@@ -689,7 +689,7 @@ class Zone(ZoneSchedule):
689
689
 
690
690
  @property
691
691
  def heating_type(self) -> str | None:
692
- """Return the type of the zone/DHW (e.g. electric_zone, stored_dhw)."""
692
+ """Get the type of the zone/DHW (e.g. electric_zone, stored_dhw)."""
693
693
 
694
694
  if self._SLUG is None: # isinstance(self, ???)
695
695
  return None
@@ -697,12 +697,13 @@ class Zone(ZoneSchedule):
697
697
 
698
698
  @property
699
699
  def name(self) -> str | None: # 0004
700
- """Return the name of the zone."""
700
+ """Get the name of the zone."""
701
701
 
702
702
  if self._gwy.msg_db:
703
703
  msgs = self._gwy.msg_db.get(
704
704
  code=Code._0004, src=self._z_id, ctx=self._z_idx
705
705
  )
706
+ _LOGGER.debug(f"Pick Zone.name from: {msgs}[0])") # DEBUG issue #317
706
707
  return msgs[0].payload.get(SZ_NAME) if msgs else None
707
708
 
708
709
  return self._msg_value(Code._0004, key=SZ_NAME) # type: ignore[no-any-return]
ramses_rf/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """RAMSES RF - a RAMSES-II protocol decoder & analyser (application layer)."""
2
2
 
3
- __version__ = "0.52.5"
3
+ __version__ = "0.53.1"
4
4
  VERSION = __version__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramses_rf
3
- Version: 0.52.5
3
+ Version: 0.53.1
4
4
  Summary: A stateful RAMSES-II protocol decoder & analyser.
5
5
  Project-URL: Homepage, https://github.com/ramses-rf/ramses_rf
6
6
  Project-URL: Bug Tracker, https://github.com/ramses-rf/ramses_rf/issues
@@ -1,55 +1,56 @@
1
- ramses_cli/__init__.py,sha256=uvGzWqOf4avvgzxJNSLFWEelIWqSZ-AeLAZzg5x58bc,397
2
- ramses_cli/client.py,sha256=h6liNKFXGsR2q9Unn81ushK53zqKgmkT0oAWNB5Jn0I,20369
1
+ ramses_cli/__init__.py,sha256=d_3uIFkK8JnWOxknrBloKCe6-vI9Ouo_KGqR4kfBQW8,417
2
+ ramses_cli/client.py,sha256=Fd6hXGBvI-i89Xu8QbBU9NW-dWlQjSer-_LYAtvJPW4,25055
3
3
  ramses_cli/debug.py,sha256=PLcz-3PjUiMVqtD_p6VqTA92eHUM58lOBFXh_qgQ_wA,576
4
- ramses_cli/discovery.py,sha256=MWoahBnAAVzfK2S7EDLsY2WYqN_ZK9L-lktrj8_4cb0,12978
4
+ ramses_cli/discovery.py,sha256=WTcoFH5hNhQ1AeOZtpdZIVYwdUfmUKlq2iBpa-KcgoI,12512
5
+ ramses_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
6
  ramses_cli/utils/cat_slow.py,sha256=AhUpM5gnegCitNKU-JGHn-DrRzSi-49ZR1Qw6lxe_t8,607
6
7
  ramses_cli/utils/convert.py,sha256=N3LxGe3_0pclijtmYW-ChqCuPTzbkoJA4XNAnoSnBk0,1806
7
- ramses_rf/__init__.py,sha256=vp2TyFGqc1fGQHsevhmaw0QEmSSCnZx7fqizKiEwHtw,1245
8
+ ramses_rf/__init__.py,sha256=AXsCK1Eh9FWeAI9D_zY_2KB0dqrTb9a5TNY1NvyQaDM,1271
8
9
  ramses_rf/binding_fsm.py,sha256=fuqvcc9YW-wr8SPH8zadpPqrHAvzl_eeWF-IBtlLppY,26632
9
10
  ramses_rf/const.py,sha256=L3z31CZ-xqno6oZp_h-67CB_5tDDqTwSWXsqRtsjMcs,5460
10
- ramses_rf/database.py,sha256=9nY1Wt2hjkBoDvDqopzPP3mPSH5-Q5XHPQuJnM2GHOw,21362
11
+ ramses_rf/database.py,sha256=AHXIx5pO0qgiHbBQxLBegnk0e1M4SFwD9Spd4eyW_eQ,21428
11
12
  ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
12
- ramses_rf/entity_base.py,sha256=Psa75zsZf6GOvXIv4c37vJDbg5iVQ1gQCADkBNOz1m8,58912
13
+ ramses_rf/entity_base.py,sha256=L47P_6CRz3tLDzOzII9AgmueKDb-Bp7Ot3vVsr8jo10,59121
13
14
  ramses_rf/exceptions.py,sha256=mt_T7irqHSDKir6KLaf6oDglUIdrw0S40JbOrWJk5jc,3657
14
- ramses_rf/gateway.py,sha256=B7BlrNHx26lIHzjgy6-4NMtohb_GATaBuucgsUUxRj4,22005
15
+ ramses_rf/gateway.py,sha256=hHsXbpeZ2Fetpu3jzv8UeUwlNf4rsxOD7eOHuLY83Xk,30101
15
16
  ramses_rf/helpers.py,sha256=TNk_QkpIOB3alOp1sqnA9LOzi4fuDCeapNlW3zTzNas,4250
16
17
  ramses_rf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- ramses_rf/schemas.py,sha256=0_qQZSGj2ixq4UCtMxoAeWklOJiM4bbBUzMsPw8gUPw,13475
18
- ramses_rf/version.py,sha256=wV7QvKiXDL06V_DHo0XYcc-6CIYMlhztVGzCh1m45D8,125
18
+ ramses_rf/schemas.py,sha256=X1GAK3kttuLMiSCUDY2s-85fgBxPeU8xiDa6gJ1I5mY,13543
19
+ ramses_rf/version.py,sha256=yMI3sQ8wyLuGx_Hm7ZYNuwbqFZ3Qhr3GJXdkdsGwh7s,125
19
20
  ramses_rf/device/__init__.py,sha256=sUbH5dhbYFXSoM_TPFRutpRutBRpup7_cQ9smPtDTy8,4858
20
21
  ramses_rf/device/base.py,sha256=Tu5I8Lj7KplfRsIBQAYjilS6YPgTyjpU8qgKugMR2Jk,18281
21
- ramses_rf/device/heat.py,sha256=T-ENFzH1AEOd0m4-TgV-Qk0TnLNir_pYmyKlJtxy6NI,54538
22
+ ramses_rf/device/heat.py,sha256=CU6GlIgjuYD21braJ_RJlS56zP47TGXNxXnZeavfEMY,54654
22
23
  ramses_rf/device/hvac.py,sha256=vdgiPiLtCAGr7CVsGhQl6XuAFkyYdQSE_2AEdCmRl2I,48502
23
24
  ramses_rf/system/__init__.py,sha256=uZLKio3gLlBzePa2aDQ1nxkcp1YXOGrn6iHTG8LiNIw,711
24
25
  ramses_rf/system/faultlog.py,sha256=GdGmVGT3137KsTlV_nhccgIFEmYu6DFsLTn4S-8JSok,12799
25
26
  ramses_rf/system/heat.py,sha256=qQmzgmyHy2x87gHAstn0ee7ZVVOq-GJIfDxCrC-6gFU,39254
26
27
  ramses_rf/system/schedule.py,sha256=Ts6tdZPTQLV5NkgwA73tPa5QUsnZNIIuYoKC-8VsXDk,18808
27
- ramses_rf/system/zones.py,sha256=qMv7CuvZUKBNLpPRXgFA70NFRStgVJcw062h5mc3Y8Q,37034
28
+ ramses_rf/system/zones.py,sha256=6VbPsOuNbGwBUuiRu8w9D1Q18SHKkuZa2YtKTE5nqlo,37110
28
29
  ramses_tx/__init__.py,sha256=sqnjM7pUGJDmec6igTtKViSB8FLX49B5gwhAmcY9ERY,3596
29
30
  ramses_tx/address.py,sha256=IuwUwZxykn3fP1UCRcv4D-zbTICBe2FJjDAFX5X6VoI,9108
30
- ramses_tx/command.py,sha256=NeqWbdaWrXjP5KEZgXFicy_1UYOoUznRHN2ZfhVyDH0,125652
31
- ramses_tx/const.py,sha256=AMwHitDq115rB24f3fzclNGC4ArMW16DbqiFWQc0U5o,30306
31
+ ramses_tx/command.py,sha256=drxmpdM4YgyPg4h0QIr1ouxK9QjfeLVgnFpDRox0CCY,125652
32
+ ramses_tx/const.py,sha256=jiE2UaGBJ5agr68EMrcEHWtVz2KMidU7c7rRYCIiaoM,33010
32
33
  ramses_tx/exceptions.py,sha256=FJSU9YkvpKjs3yeTqUJX1o3TPFSe_B01gRGIh9b3PNc,2632
33
34
  ramses_tx/fingerprints.py,sha256=nfftA1E62HQnb-eLt2EqjEi_la0DAoT0wt-PtTMie0s,11974
34
35
  ramses_tx/frame.py,sha256=GzNsXr15YLeidJYGtk_xPqsZQh4ehDDlUCtT6rTDhT8,22046
35
- ramses_tx/gateway.py,sha256=Fl6EqAUU2DnLOiA2_87sS7VPBEyxA1a_ICCmg55WMMA,11494
36
- ramses_tx/helpers.py,sha256=VIPSw0lrDrE3S92YchzeRo7G2AlQ_vE8OuYUF-rlpQw,33616
36
+ ramses_tx/gateway.py,sha256=H4JtIkp7JFMyZZ76sij67rTbjc1i3iWaIf6tuxIpHAg,11529
37
+ ramses_tx/helpers.py,sha256=96OvSOWYuMcr89_c-3dRnqHZaMOctCO94uo1hETh3bc,33613
37
38
  ramses_tx/logger.py,sha256=1iKRHKUaqHqGd76CkE_6mCVR0sYODtxshRRwfY61fTk,10426
38
- ramses_tx/message.py,sha256=HUSNiBgGlnWEBeBqb4K28GcU5oB7RbS9a0JC2p9SGe4,13676
39
+ ramses_tx/message.py,sha256=zsyDQztSUYeqj3-P598LSmy9ODQY2BUCzWxSoZds6bM,13953
39
40
  ramses_tx/opentherm.py,sha256=58PXz9l5x8Ou6Fm3y-R_UnGHCYahoi2RKIDdYStUMzk,42378
40
41
  ramses_tx/packet.py,sha256=_nzuInS_WhdOI26SYvgsdDqIaDvVNguc2YDwdPOVCbU,7661
41
- ramses_tx/parsers.py,sha256=qQz8VOKaH26MUvo9AfUwhrnbH7VwXLxFc2gKJYvjXvY,148571
42
- ramses_tx/protocol.py,sha256=ApA__ubfxM63c1OAxRcH6fhjMQTdC07SnSH9cGfWO2U,32427
43
- ramses_tx/protocol_fsm.py,sha256=ZKtehCr_4TaDdfdlfidFLJaOVTYtaEq5h4tLqNIhb9s,26827
42
+ ramses_tx/parsers.py,sha256=vHaVdapzW9TtQhCjR9XuaLLkp5ZMXKmTm05zbu0ekW0,148576
43
+ ramses_tx/protocol.py,sha256=nBPKCD1tcGp_FiX0qhsY0XoGO_h87w5cYywBjSpum4w,33048
44
+ ramses_tx/protocol_fsm.py,sha256=o9vLvlXor3LkPgsY1zii5P1R01GzYLf_PECDdoxtC24,27520
44
45
  ramses_tx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
46
  ramses_tx/ramses.py,sha256=vp748Tf_a-56OMM8CWDA2ZktRfTuj0QVyPRcnsOstSM,53983
46
- ramses_tx/schemas.py,sha256=bqKW_V0bR6VbBD8ZQiBExNtVdXs0fryVKe3GEhupgIo,13424
47
- ramses_tx/transport.py,sha256=-Ikwj_Hy6JEnQPS9gBalV3H5AKQd3ATxKskR7Dy7_sE,59774
47
+ ramses_tx/schemas.py,sha256=Hrmf_q9bAZtkKJzGu6GtUO0QV_-K9i4L99EzGWR13eE,13408
48
+ ramses_tx/transport.py,sha256=hktWSDOzKUME967w58JB4z3MgmCMiQ0QZI0x4-Awaas,76419
48
49
  ramses_tx/typed_dicts.py,sha256=w-0V5t2Q3GiNUOrRAWiW9GtSwbta_7luME6GfIb1zhI,10869
49
50
  ramses_tx/typing.py,sha256=eF2SlPWhNhEFQj6WX2AhTXiyRQVXYnFutiepllYl2rI,5042
50
- ramses_tx/version.py,sha256=j7HXmU97PXKJClRv1saySQ_JZYHAxBznxUTdWH64uHE,123
51
- ramses_rf-0.52.5.dist-info/METADATA,sha256=gGSPoi37fljrfFxDIVHXFUU5RuzGu6wwwD0JbvdwiVQ,4179
52
- ramses_rf-0.52.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
53
- ramses_rf-0.52.5.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
54
- ramses_rf-0.52.5.dist-info/licenses/LICENSE,sha256=-Kc35W7l1UkdiQ4314_yVWv7vDDrg7IrJfMLUiq6Nfs,1074
55
- ramses_rf-0.52.5.dist-info/RECORD,,
51
+ ramses_tx/version.py,sha256=g3EPkHx--jzBHNq9oau7n6DOcVjdbcDWz5m13ZpzvEQ,123
52
+ ramses_rf-0.53.1.dist-info/METADATA,sha256=OC2EXqy23YPE-jCqrXSdLM4cN5_7a9M3fBWh15r3kqg,4179
53
+ ramses_rf-0.53.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
54
+ ramses_rf-0.53.1.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
55
+ ramses_rf-0.53.1.dist-info/licenses/LICENSE,sha256=ptVutrtSMr7X-ek6LduiD8Cce4JsNn_8sR8MYlm-fvo,1086
56
+ ramses_rf-0.53.1.dist-info/RECORD,,
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021-2025 David Bonnes
3
+ Copyright (c) 2021-2026 D. Bonnes and E. Broerse
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
ramses_tx/command.py CHANGED
@@ -1866,7 +1866,7 @@ class Command(Frame):
1866
1866
  The actual number of available zones depends on the controller configuration.
1867
1867
  Requesting a non-existent zone will typically result in no response.
1868
1868
  """
1869
- return cls.from_attrs(W_, ctl_id, Code._2309, _check_idx(zone_idx))
1869
+ return cls.from_attrs(RQ, ctl_id, Code._2309, _check_idx(zone_idx))
1870
1870
 
1871
1871
  @classmethod # constructor for W|2309
1872
1872
  def set_zone_setpoint(