ramses-rf 0.53.5__py3-none-any.whl → 0.53.6__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/dispatcher.py CHANGED
@@ -270,7 +270,11 @@ def process_msg(gwy: Gateway, msg: Message) -> None:
270
270
  )
271
271
 
272
272
  except (AttributeError, LookupError, TypeError, ValueError) as err:
273
- _LOGGER.exception("%s < %s(%s)", msg._pkt, err.__class__.__name__, err)
273
+ if getattr(gwy.config, "enforce_strict_handling", False):
274
+ raise
275
+ _LOGGER.warning(
276
+ "%s < %s(%s)", msg._pkt, err.__class__.__name__, err, exc_info=True
277
+ )
274
278
 
275
279
  else:
276
280
  logger_xxxx(msg)
ramses_rf/schemas.py CHANGED
@@ -245,6 +245,7 @@ SCH_GLOBAL_SCHEMAS = vol.Schema(SCH_GLOBAL_SCHEMAS_DICT, extra=vol.PREVENT_EXTRA
245
245
  # 4/7: Gateway (parser/state) configuration
246
246
  SZ_DISABLE_DISCOVERY: Final = "disable_discovery"
247
247
  SZ_ENABLE_EAVESDROP: Final = "enable_eavesdrop"
248
+ SZ_ENFORCE_STRICT_HANDLING: Final = "enforce_strict_handling"
248
249
  SZ_MAX_ZONES: Final = "max_zones" # TODO: move to TCS-attr from GWY-layer
249
250
  SZ_REDUCE_PROCESSING: Final = "reduce_processing"
250
251
  SZ_USE_ALIASES: Final = "use_aliases" # use friendly device names from known_list
@@ -253,6 +254,7 @@ SZ_USE_NATIVE_OT: Final = "use_native_ot" # favour OT (3220s) over RAMSES
253
254
  SCH_GATEWAY_DICT = {
254
255
  vol.Optional(SZ_DISABLE_DISCOVERY, default=False): bool,
255
256
  vol.Optional(SZ_ENABLE_EAVESDROP, default=False): bool,
257
+ vol.Optional(SZ_ENFORCE_STRICT_HANDLING, default=False): bool,
256
258
  vol.Optional(SZ_MAX_ZONES, default=DEFAULT_MAX_ZONES): vol.All(
257
259
  int, vol.Range(min=1, max=16)
258
260
  ), # NOTE: no default
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.53.5"
3
+ __version__ = "0.53.6"
4
4
  VERSION = __version__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramses_rf
3
- Version: 0.53.5
3
+ Version: 0.53.6
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
@@ -62,11 +62,11 @@ To install the `ramses_rf` command line client:
62
62
  ```
63
63
  git clone https://github.com/ramses-rf/ramses_rf
64
64
  cd ramses_rf
65
- pip install -r requirements.txt
65
+ pip install -r requirements/requirements.txt
66
66
  pip install -e .
67
67
  ```
68
68
 
69
- The CLI is called ``client.py`` and is included in the code root.
69
+ The CLI is called `client.py` and is included in the code root.
70
70
  It has options to monitor and parse Ramses-II traffic to screen or a log file, and to parse a file containing Ramses-II messages to the screen.
71
71
  See the [client.py CLI wiki page](https://github.com/ramses-rf/ramses_rf/wiki/2.-The-client.py-command-line) for instructions.
72
72
 
@@ -9,15 +9,15 @@ ramses_rf/__init__.py,sha256=AXsCK1Eh9FWeAI9D_zY_2KB0dqrTb9a5TNY1NvyQaDM,1271
9
9
  ramses_rf/binding_fsm.py,sha256=fuqvcc9YW-wr8SPH8zadpPqrHAvzl_eeWF-IBtlLppY,26632
10
10
  ramses_rf/const.py,sha256=L3z31CZ-xqno6oZp_h-67CB_5tDDqTwSWXsqRtsjMcs,5460
11
11
  ramses_rf/database.py,sha256=eARZ8F5lcITK6d_MfvozmMxSGNkiy1kbtAh0NOIHMoc,24066
12
- ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
12
+ ramses_rf/dispatcher.py,sha256=pHNrXOLeAp6i9TUZEPNOB1AevlKkQyUfX2dkr-EzrDw,11517
13
13
  ramses_rf/entity_base.py,sha256=Lv4N3dyIRfsz_5Ztgcu4bc49UE-N4c1VuN732_HQp-g,59255
14
14
  ramses_rf/exceptions.py,sha256=mt_T7irqHSDKir6KLaf6oDglUIdrw0S40JbOrWJk5jc,3657
15
15
  ramses_rf/gateway.py,sha256=BsS3gyFcSOCLuzQ_OxgyqHTcn2wAVsEJaV6B5PbYre0,31087
16
16
  ramses_rf/helpers.py,sha256=TNk_QkpIOB3alOp1sqnA9LOzi4fuDCeapNlW3zTzNas,4250
17
17
  ramses_rf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ramses_rf/schemas.py,sha256=X1GAK3kttuLMiSCUDY2s-85fgBxPeU8xiDa6gJ1I5mY,13543
18
+ ramses_rf/schemas.py,sha256=k0IN2OvIgNGZ9lGkFMuM81jcjUqWvdGNfaccDBwfh_8,13672
19
19
  ramses_rf/storage.py,sha256=ZFUhvgsWRCVS1_r6LL032XFEJwZVp1RAK8Nfba8nf7o,7052
20
- ramses_rf/version.py,sha256=HfkMfk1rLOZbqMvqEgE1o6j48RTa0l-dHJ-_7nrquYM,125
20
+ ramses_rf/version.py,sha256=Qfhbwe6tvYg3scyTH0mknbjnE0tb-XQ19FcHCCdu8CM,125
21
21
  ramses_rf/device/__init__.py,sha256=sUbH5dhbYFXSoM_TPFRutpRutBRpup7_cQ9smPtDTy8,4858
22
22
  ramses_rf/device/base.py,sha256=Tu5I8Lj7KplfRsIBQAYjilS6YPgTyjpU8qgKugMR2Jk,18281
23
23
  ramses_rf/device/heat.py,sha256=CU6GlIgjuYD21braJ_RJlS56zP47TGXNxXnZeavfEMY,54654
@@ -42,16 +42,16 @@ ramses_tx/opentherm.py,sha256=58PXz9l5x8Ou6Fm3y-R_UnGHCYahoi2RKIDdYStUMzk,42378
42
42
  ramses_tx/packet.py,sha256=_nzuInS_WhdOI26SYvgsdDqIaDvVNguc2YDwdPOVCbU,7661
43
43
  ramses_tx/parsers.py,sha256=ALUoi21ewd_GZHvxq4051AcVwETOTgVr5feWaY7zdls,148659
44
44
  ramses_tx/protocol.py,sha256=E62vWb8qY7_SB5tb_NcywAED4d9NJJJ-1NgMaK3HG5s,33198
45
- ramses_tx/protocol_fsm.py,sha256=o9vLvlXor3LkPgsY1zii5P1R01GzYLf_PECDdoxtC24,27520
45
+ ramses_tx/protocol_fsm.py,sha256=uT0jLuTsz_6zUJSvdMG200xbYQIFgqfzJ0t3l5bTTTc,27741
46
46
  ramses_tx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  ramses_tx/ramses.py,sha256=89EFL91zwnArefVcEVw3KoyqF92d3r3aBoJapMNAT0I,54389
48
- ramses_tx/schemas.py,sha256=Hrmf_q9bAZtkKJzGu6GtUO0QV_-K9i4L99EzGWR13eE,13408
49
- ramses_tx/transport.py,sha256=RIrcNrJwiKB_xmJLgG4Z--V2d83PLsJnLXZK-WFFgsA,76568
48
+ ramses_tx/schemas.py,sha256=Bh877L2lmsrtq86ygQEnlbalcyRSEvfsjRnub-9P6X4,13495
49
+ ramses_tx/transport.py,sha256=W2y6em3PQytTYnREZsm2b2-FQeZQCciY2UprBa8-PM8,77057
50
50
  ramses_tx/typed_dicts.py,sha256=w-0V5t2Q3GiNUOrRAWiW9GtSwbta_7luME6GfIb1zhI,10869
51
51
  ramses_tx/typing.py,sha256=eF2SlPWhNhEFQj6WX2AhTXiyRQVXYnFutiepllYl2rI,5042
52
- ramses_tx/version.py,sha256=j6vK_nd2r1udUd4SmgheUwwD64f35BfwQoHsuHcFaYs,123
53
- ramses_rf-0.53.5.dist-info/METADATA,sha256=m53OVOic5uCyW6ScGRSjARqKekXiTbJ4T2KLIMDe3LE,4179
54
- ramses_rf-0.53.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
55
- ramses_rf-0.53.5.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
56
- ramses_rf-0.53.5.dist-info/licenses/LICENSE,sha256=ptVutrtSMr7X-ek6LduiD8Cce4JsNn_8sR8MYlm-fvo,1086
57
- ramses_rf-0.53.5.dist-info/RECORD,,
52
+ ramses_tx/version.py,sha256=wG4Me_pyFvCD82_aAFqAut5OXMh7AJllw7_gB6uBe5M,123
53
+ ramses_rf-0.53.6.dist-info/METADATA,sha256=RDKvYo4RMR13qU0XyXjxv9HumiHUaZeXfaWQc4SlLNs,4190
54
+ ramses_rf-0.53.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
55
+ ramses_rf-0.53.6.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
56
+ ramses_rf-0.53.6.dist-info/licenses/LICENSE,sha256=ptVutrtSMr7X-ek6LduiD8Cce4JsNn_8sR8MYlm-fvo,1086
57
+ ramses_rf-0.53.6.dist-info/RECORD,,
ramses_tx/protocol_fsm.py CHANGED
@@ -145,13 +145,20 @@ class ProtocolContext:
145
145
  # nope, was not successful, so multiplier should be incremented...
146
146
  self._multiplier = min(3, old_val + 1)
147
147
 
148
+ if self._cmd_tx_count < 3:
149
+ level = logging.DEBUG
150
+ elif self._cmd_tx_count == 3:
151
+ level = logging.INFO
152
+ else:
153
+ level = logging.WARNING
154
+
148
155
  if isinstance(self._state, WantEcho):
149
- _LOGGER.warning(
150
- f"Timeout expired waiting for echo: {self} (delay={delay})"
156
+ _LOGGER.log(
157
+ level, f"Timeout expired waiting for echo: {self} (delay={delay})"
151
158
  )
152
159
  else: # isinstance(self._state, WantRply):
153
- _LOGGER.warning(
154
- f"Timeout expired waiting for reply: {self} (delay={delay})"
160
+ _LOGGER.log(
161
+ level, f"Timeout expired waiting for reply: {self} (delay={delay})"
155
162
  )
156
163
 
157
164
  assert isinstance(self.is_sending, bool), (
ramses_tx/schemas.py CHANGED
@@ -407,6 +407,7 @@ def select_device_filter_mode(
407
407
  # 5/5: Gateway (engine) configuration
408
408
 
409
409
  SZ_DISABLE_SENDING: Final = "disable_sending"
410
+ SZ_AUTOSTART: Final = "autostart"
410
411
  SZ_DISABLE_QOS: Final = "disable_qos"
411
412
  SZ_ENFORCE_KNOWN_LIST: Final[str] = f"enforce_{SZ_KNOWN_LIST}"
412
413
  SZ_EVOFW_FLAG: Final = "evofw_flag"
@@ -418,6 +419,7 @@ SZ_USE_REGEX: Final = "use_regex"
418
419
 
419
420
  SCH_ENGINE_DICT = {
420
421
  vol.Optional(SZ_DISABLE_SENDING, default=False): bool,
422
+ vol.Optional(SZ_AUTOSTART, default=False): bool,
421
423
  vol.Optional(SZ_DISABLE_QOS, default=None): vol.Any(
422
424
  None, # None is selective QoS (e.g. QoS only for bindings, schedule, etc.)
423
425
  bool,
ramses_tx/transport.py CHANGED
@@ -1278,8 +1278,9 @@ class PortTransport(_RegHackMixin, _FullTransport, _PortTransportAbstractor): #
1278
1278
 
1279
1279
  super()._close(exc)
1280
1280
 
1281
- if self._init_task:
1282
- self._init_task.cancel()
1281
+ # Use getattr because _init_task may not be set if initialization failed
1282
+ if init_task := getattr(self, "_init_task", None):
1283
+ init_task.cancel()
1283
1284
 
1284
1285
  if self._leaker_task:
1285
1286
  self._leaker_task.cancel()
@@ -1810,6 +1811,7 @@ class CallbackTransport(_FullTransport, _CallbackTransportAbstractor):
1810
1811
  protocol: RamsesProtocolT,
1811
1812
  io_writer: Callable[[str], Awaitable[None]],
1812
1813
  disable_sending: bool = False,
1814
+ autostart: bool = False,
1813
1815
  **kwargs: Any,
1814
1816
  ) -> None:
1815
1817
  """Initialize the callback transport.
@@ -1820,6 +1822,8 @@ class CallbackTransport(_FullTransport, _CallbackTransportAbstractor):
1820
1822
  :type io_writer: Callable[[str], Awaitable[None]]
1821
1823
  :param disable_sending: Whether to disable sending, defaults to False.
1822
1824
  :type disable_sending: bool, optional
1825
+ :param autostart: Whether to start reading immediately, defaults to False.
1826
+ :type autostart: bool, optional
1823
1827
  """
1824
1828
  # Pass kwargs up the chain. _ReadTransport will extract 'loop' if present.
1825
1829
  # _BaseTransport will pass 'loop' to _CallbackTransportAbstractor, which consumes it.
@@ -1834,9 +1838,11 @@ class CallbackTransport(_FullTransport, _CallbackTransportAbstractor):
1834
1838
  # Section 6.1: Object Lifecycle Logging
1835
1839
  _LOGGER.info(f"CallbackTransport created with io_writer={io_writer}")
1836
1840
 
1837
- # NOTE: connection_made is NOT called here. It must be triggered
1838
- # externally (e.g. by the Bridge) via the protocol methods once
1839
- # the external connection is ready.
1841
+ # Handshake: Notify protocol immediately (Safe: idempotent)
1842
+ self._protocol.connection_made(self, ramses=True)
1843
+
1844
+ if autostart:
1845
+ self.resume_reading()
1840
1846
 
1841
1847
  async def write_frame(self, frame: str, disable_tx_limits: bool = False) -> None:
1842
1848
  """Process a frame for transmission by passing it to the external writer.
@@ -1879,19 +1885,19 @@ class CallbackTransport(_FullTransport, _CallbackTransportAbstractor):
1879
1885
  :type dtm: str | None, optional
1880
1886
  """
1881
1887
  _LOGGER.debug(
1882
- f"Received frame from external source: frame='{frame}', timestamp={dtm}"
1888
+ f"Received frame from external source: frame={repr(frame)}, timestamp={dtm}"
1883
1889
  )
1884
1890
 
1885
- # Section 4.2: Circuit Breaker implementation (Packet gating)
1891
+ # Circuit Breaker implementation (Packet gating)
1886
1892
  if not self._reading:
1887
1893
  _LOGGER.debug(f"Dropping received frame (transport paused): {repr(frame)}")
1888
1894
  return
1889
1895
 
1890
1896
  dtm = dtm or dt_now().isoformat()
1891
1897
 
1892
- # Section 6.1: Boundary Logging (Incoming)
1898
+ # Boundary Logging (Incoming)
1893
1899
  _LOGGER.debug(
1894
- f"Ingesting frame into transport: frame='{frame}', timestamp={dtm}"
1900
+ f"Ingesting frame into transport: frame={repr(frame)}, timestamp={dtm}"
1895
1901
  )
1896
1902
 
1897
1903
  # Pass to the standard processing pipeline
@@ -1949,7 +1955,7 @@ async def transport_factory(
1949
1955
  extra: dict[str, Any] | None = None,
1950
1956
  loop: asyncio.AbstractEventLoop | None = None,
1951
1957
  log_all: bool = False,
1952
- **kwargs: Any, # HACK: odd/misc params
1958
+ **kwargs: Any, # HACK: odd/misc params, inc. autostart
1953
1959
  ) -> RamsesTransportT:
1954
1960
  """Create and return a Ramses-specific async packet Transport.
1955
1961
 
@@ -1980,11 +1986,18 @@ async def transport_factory(
1980
1986
  :raises exc.TransportSourceInvalid: If the packet source is invalid or multiple sources are specified.
1981
1987
  """
1982
1988
 
1989
+ # Extract autostart (default to False if missing), used in transport_constructor only
1990
+ autostart = kwargs.pop("autostart", False)
1991
+
1983
1992
  # If a constructor is provided, delegate entirely to it.
1984
1993
  if transport_constructor:
1985
1994
  _LOGGER.debug("transport_factory: Delegating to external transport_constructor")
1986
1995
  return await transport_constructor(
1987
- protocol, disable_sending=disable_sending, extra=extra, **kwargs
1996
+ protocol,
1997
+ disable_sending=disable_sending,
1998
+ extra=extra,
1999
+ autostart=autostart, # <--- Pass it explicitly
2000
+ **kwargs,
1988
2001
  )
1989
2002
 
1990
2003
  # kwargs are specific to a transport. The above transports have:
ramses_tx/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """RAMSES RF - a RAMSES-II protocol decoder & analyser (transport layer)."""
2
2
 
3
- __version__ = "0.53.5"
3
+ __version__ = "0.53.6"
4
4
  VERSION = __version__