ramses-rf 0.52.1__py3-none-any.whl → 0.52.3__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/entity_base.py CHANGED
@@ -67,8 +67,7 @@ if TYPE_CHECKING:
67
67
 
68
68
 
69
69
  _QOS_TX_LIMIT = 12 # TODO: needs work
70
- _ID_SLICE = 9 # base address only, legacy _msgs 9
71
- _SQL_SLICE = 12 # msg_db dst field query 12
70
+ _ID_SLICE = 9
72
71
  _SZ_LAST_PKT: Final = "last_msg"
73
72
  _SZ_NEXT_DUE: Final = "next_due"
74
73
  _SZ_TIMEOUT: Final = "timeout"
@@ -172,7 +171,31 @@ class _Entity:
172
171
 
173
172
 
174
173
  class _MessageDB(_Entity):
175
- """Maintain/utilize an entity's state database."""
174
+ """Maintain/utilize an entity's state database.
175
+
176
+ EntityBase msg_db query methods
177
+
178
+ (ix = database.py.MessageIndex method)
179
+
180
+ .. table:: Database Query Methods
181
+ :widths: auto
182
+
183
+ ==== ====================== ==================== ============ ========== ==========
184
+ e. method name args returns uses used by
185
+ ==== ====================== ==================== ============ ========== ==========
186
+ e1 _get_msg_by_hdr hdr Message i3 discover
187
+ e2 _msg_value code(s), Msg, args dict[k,v] e3,e4
188
+ e3 _msg_value_code code, verb, key dict[k,v] e4,e5,e6 e6
189
+ e4 _msg_value_msg Msg, (code) dict[k,v] e2,e3
190
+ e5 _msg_qry_by_code_key code, key, (verb=) e6,
191
+ e6 _msg_value_qry_by_code key code, key str/float e3,e5
192
+ e7 _msg_qry sql e8
193
+ e8 _msg_count sql e7
194
+ e9 supported_cmds list(Codes) i7
195
+ e10 _msgs() i5
196
+ ==== ====================== ==================== ============ ========== ==========
197
+
198
+ """
176
199
 
177
200
  _gwy: Gateway
178
201
  ctl: Controller
@@ -220,7 +243,14 @@ class _MessageDB(_Entity):
220
243
  return # don't store the rest
221
244
 
222
245
  if self._gwy.msg_db: # central SQLite MessageIndex
223
- self._gwy.msg_db.add(msg)
246
+ _LOGGER.debug(
247
+ "For %s (z_id %s) add msg %s, src %s, dst %s to msg_db.",
248
+ self.id,
249
+ self._z_id,
250
+ msg,
251
+ msg.src,
252
+ msg.dst,
253
+ )
224
254
  debug_code: Code = Code._3150
225
255
  if msg.code == debug_code and msg.src.id.startswith("01:"):
226
256
  _LOGGER.debug(
@@ -234,6 +264,7 @@ class _MessageDB(_Entity):
234
264
  # msg.src = 01:073976 (CTL)
235
265
  # Added msg from 01:073976 (CTL) with code 0005 to _gwy.msg_db
236
266
  # query is for: 01:073976 < no suffix, extended lookup to [:12] chars
267
+ self._gwy.msg_db.add(msg)
237
268
 
238
269
  # ignore any replaced message that might be returned
239
270
  else: # TODO(eb): remove Q1 2026
@@ -260,7 +291,7 @@ class _MessageDB(_Entity):
260
291
 
261
292
  @property
262
293
  def _msg_list(self) -> list[Message]:
263
- """Return a flattened list of all messages logged on device."""
294
+ """Return a flattened list of all messages logged on this device."""
264
295
  # (only) used in gateway.py#get_state() and in tests/tests/test_eavesdrop_schema.py
265
296
  if self._gwy.msg_db:
266
297
  msg_list_qry: list[Message] = []
@@ -271,7 +302,14 @@ class _MessageDB(_Entity):
271
302
  # safeguard against lookup failures ("sim" packets?)
272
303
  msg_list_qry.append(self._msgs[c])
273
304
  else:
274
- _LOGGER.debug("Could not fetch self._msgs[%s]", c)
305
+ # evohome has these errors
306
+ # _msg_list could not fetch self._msgs[7FFF] for 18:072981 (z_id 18:072981)
307
+ _LOGGER.debug(
308
+ "_msg_list could not fetch self._msgs[%s] for %s (z_id %s)",
309
+ c,
310
+ self.id,
311
+ self._z_id,
312
+ )
275
313
  return msg_list_qry
276
314
  # else create from legacy nested dict
277
315
  return [m for c in self._msgz.values() for v in c.values() for m in v.values()]
@@ -316,32 +354,7 @@ class _MessageDB(_Entity):
316
354
  with contextlib.suppress(KeyError):
317
355
  del obj._msgz_[msg.code][msg.verb][msg._pkt._ctx]
318
356
 
319
- # EntityBase msg_db query methods > copy to docs/source/ramses_rf.rst
320
- # (ix = database.py.MessageIndex method)
321
- #
322
- # +----+----------------------+--------------------+------------+----------+----------+
323
- # | e. |method name | args | returns | uses | used by |
324
- # +====+======================+====================+============+==========+==========+
325
- # | e1 | _get_msg_by_hdr | hdr | Message | i3 | discover |
326
- # +----+----------------------+--------------------+------------+----------+----------+
327
- # | e2 | _msg_value | code(s), Msg, args | dict[k,v] | e3,e4 | |
328
- # +----+----------------------+--------------------+------------+----------+----------+
329
- # | e3 | _msg_value_code | code, verb, key | dict[k,v] | e4,e5,e6 | e6 |
330
- # +----+----------------------+--------------------+------------+----------+----------+
331
- # | e4 | _msg_value_msg | Msg, (code) | dict[k,v] | | e2,e3 |
332
- # +----+----------------------+--------------------+------------+----------+----------+
333
- # | e5 | _msg_qry_by_code_key | code, key, (verb=) | | | e6, |
334
- # +----+----------------------+--------------------+------------+----------+----------+
335
- # | e6 | _msg_value_qry_by_code_key | code, key | str/float | e3,e5 | |
336
- # +----+----------------------+--------------------+------------+----------+----------+
337
- # | e7 | _msg_qry | sql | | | e8 |
338
- # +----+----------------------+--------------------+------------+----------+----------+
339
- # | e8 | _msg_count | sql | | e7 | |
340
- # +----+----------------------+--------------------+------------+----------+----------+
341
- # | e9 | supported_cmds | | list(Codes)| i7 | |
342
- # +----+----------------------+--------------------+------------+----------+----------+
343
- # | e10| _msgs() | | | i5 | |
344
- # +----+----------------------+--------------------+------------+----------+----------+
357
+ ### entity_base query methods
345
358
 
346
359
  def _get_msg_by_hdr(self, hdr: HeaderT) -> Message | None:
347
360
  """Return a msg, if any, that matches a given header."""
@@ -417,7 +430,7 @@ class _MessageDB(_Entity):
417
430
  :param code: filter messages by Code or a tuple of Codes, optional
418
431
  :param verb: filter on I, RQ, RP, optional, only with a single Code
419
432
  :param key: value keyword to retrieve, not together with verb RQ
420
- :param kwargs: not used for now
433
+ :param kwargs: extra filter, e.g. zone_idx='01'
421
434
  :return: a dict containing key: value pairs, or a list of those
422
435
  """
423
436
  assert not isinstance(code, tuple) or verb is None, (
@@ -433,7 +446,9 @@ class _MessageDB(_Entity):
433
446
  key = None
434
447
  try:
435
448
  if self._gwy.msg_db: # central SQLite MessageIndex, use verb= kwarg
436
- code = Code(self._msg_qry_by_code_key(code, key, verb=verb))
449
+ code = Code(
450
+ self._msg_qry_by_code_key(code, key, **kwargs, verb=verb)
451
+ )
437
452
  msg = self._msgs.get(code)
438
453
  else: # deprecated lookup in nested _msgz
439
454
  msgs = self._msgz[code][verb]
@@ -445,7 +460,9 @@ class _MessageDB(_Entity):
445
460
  msgs = [m for m in self._msgs.values() if m.code in code]
446
461
  msg = max(msgs) if msgs else None
447
462
  # return highest = latest? value found in code:value pairs
448
- else:
463
+ else: # single Code
464
+ # for Zones, this doesn't work, returns first result = often wrong
465
+ # TODO fix in _msg_qry_by_code_key()
449
466
  msg = self._msgs.get(code)
450
467
 
451
468
  return self._msg_value_msg(msg, key=key, **kwargs)
@@ -520,19 +537,37 @@ class _MessageDB(_Entity):
520
537
 
521
538
  :return: list of Codes or empty list when query returned empty
522
539
  """
540
+
523
541
  if self._gwy.msg_db:
524
542
  # SQLite query on MessageIndex
525
- sql = """
526
- SELECT code from messages WHERE verb in (' I', 'RP')
527
- AND (src = ? OR dst = ?)
528
- """
529
543
  res: list[Code] = []
544
+ if self.id[_ID_SLICE:] == "_HW":
545
+ sql = """
546
+ SELECT code from messages WHERE
547
+ verb in (' I', 'RP')
548
+ AND (src = ? OR dst = ?)
549
+ AND (ctx IN ('FC', 'FA', 'F9', 'FA') OR plk LIKE ?)
550
+ """
551
+ _ctx_qry = "%dhw_idx%" # syntax error ?
552
+ else:
553
+ sql = """
554
+ SELECT code from messages WHERE
555
+ verb in (' I', 'RP')
556
+ AND (src = ? OR dst = ?)
557
+ AND ctx LIKE ?
558
+ """
559
+ _ctx_qry = f"%{self.id[_ID_SLICE + 1 :]}%"
530
560
 
531
561
  for rec in self._gwy.msg_db.qry_field(
532
- sql, (self.id[:_SQL_SLICE], self.id[:_SQL_SLICE])
562
+ sql, (self.id[:_ID_SLICE], self.id[:_ID_SLICE], _ctx_qry)
533
563
  ):
534
- _LOGGER.debug("Fetched from index: %s", rec[0])
535
- # Example: "Fetched from index: code 1FD4"
564
+ _LOGGER.debug(
565
+ "Fetched from index: %s for %s (z_id %s)",
566
+ rec[0],
567
+ self.id,
568
+ self._z_id,
569
+ )
570
+ # Example: "Fetched from index: code 1FD4 for 01:123456 (z_id 01)"
536
571
  res.append(Code(str(rec[0])))
537
572
  return res
538
573
  else:
@@ -549,40 +584,58 @@ class _MessageDB(_Entity):
549
584
  Retrieve from the MessageIndex the most current Code for a code(s) &
550
585
  keyword combination involving this device.
551
586
 
552
- :param code: (optional) a message Code to use, e.g. 31DA or a tuple of Codes
587
+ :param code: (optional) a message Code to use, e.g. Code._31DA or a tuple of Codes
553
588
  :param key: (optional) message keyword to fetch, e.g. SZ_HUMIDITY
554
589
  :param kwargs: optional verb='vb' single verb
555
590
  :return: Code of most recent query result message or None when query returned empty
556
591
  """
557
592
  if self._gwy.msg_db:
558
- code_qry: str = ""
593
+ code_qry: str = "= "
559
594
  if code is None:
560
- code_qry = "*"
595
+ code_qry = "LIKE '%'" # wildcard
561
596
  elif isinstance(code, tuple):
562
597
  for cd in code:
563
598
  code_qry += f"'{str(cd)}' OR code = '"
564
599
  code_qry = code_qry[:-13] # trim last OR
565
600
  else:
566
- code_qry = str(code)
567
- key = "*" if key is None else f"%{key}%"
601
+ code_qry += str(code)
568
602
  if kwargs["verb"] and kwargs["verb"] in (" I", "RP"):
569
603
  vb = f"('{str(kwargs['verb'])}',)"
570
604
  else:
571
605
  vb = "(' I', 'RP',)"
606
+ ctx_qry = "%"
607
+ if kwargs["zone_idx"]:
608
+ ctx_qry = f"%{kwargs['zone_idx']}%"
609
+ elif kwargs["dhw_idx"]: # DHW
610
+ ctx_qry = f"%{kwargs['dhw_idx']}%"
611
+ key_qry = "%" if key is None else f"%{key}%"
612
+
572
613
  # SQLite query on MessageIndex
573
614
  sql = """
574
- SELECT dtm, code from messages WHERE verb in ?
615
+ SELECT dtm, code from messages WHERE
616
+ verb in ?
575
617
  AND (src = ? OR dst = ?)
576
- AND (code = ?)
618
+ AND (code ?)
619
+ AND (ctx LIKE ?)
577
620
  AND (plk LIKE ?)
578
621
  """
579
622
  latest: dt = dt(0, 0, 0)
580
623
  res = None
581
624
 
582
625
  for rec in self._gwy.msg_db.qry_field(
583
- sql, (vb, self.id[:_SQL_SLICE], self.id[:_SQL_SLICE], code_qry, key)
626
+ sql,
627
+ (
628
+ vb,
629
+ self.id[:_ID_SLICE],
630
+ self.id[:_ID_SLICE],
631
+ code_qry,
632
+ ctx_qry,
633
+ key_qry,
634
+ ),
584
635
  ):
585
- _LOGGER.debug("Fetched from index: %s", rec)
636
+ _LOGGER.debug(
637
+ "_msg_qry_by_code_key fetched rec: %s, code: %s", rec, code_qry
638
+ )
586
639
  assert isinstance(rec[0], dt) # mypy hint
587
640
  if rec[0] > latest: # dtm, only use most recent
588
641
  res = Code(rec[1])
@@ -641,7 +694,7 @@ class _MessageDB(_Entity):
641
694
  # """SELECT code from messages WHERE verb in (' I', 'RP') AND (src = ? OR dst = ?)
642
695
  # AND (code = '31DA' OR ...) AND (plk LIKE '%{SZ_FAN_INFO}%' OR ...)""" = 2 params
643
696
  for rec in self._gwy.msg_db.qry_field(
644
- sql, (self.id[:_SQL_SLICE], self.id[:_SQL_SLICE])
697
+ sql, (self.id[:_ID_SLICE], self.id[:_ID_SLICE])
645
698
  ):
646
699
  _pl = self._msgs[Code(rec[0])].payload
647
700
  # add payload dict to res(ults)
@@ -681,24 +734,38 @@ class _MessageDB(_Entity):
681
734
  # _LOGGER.warning("Missing MessageIndex")
682
735
  # raise NotImplementedError
683
736
 
684
- if self.id[:3] == "18:": # HGI, confirm this is correct, tests suggest so
685
- return {}
737
+ # if self.id[:3] == "18:": # HGI, confirm this is correct, tests suggest so
738
+ # return {}
686
739
 
687
- sql = """
688
- SELECT dtm from messages WHERE verb in (' I', 'RP') AND (src = ? OR dst = ?)
689
- """
690
-
691
- # handy routine to debug dict creation, see test_systems.py
740
+ # a routine to debug dict creation, see test_systems.py:
692
741
  # print(f"Create _msgs for {self.id}:")
693
742
  # results = self._gwy.msg_db._cu.execute("SELECT dtm, src, code from messages WHERE verb in (' I', 'RP') and code is '3150'")
694
743
  # for r in results:
695
744
  # print(r)
696
745
 
697
- _msg_dict = { # ? use ctx (context) instead of just the address?
746
+ if self.id[_ID_SLICE:] == "_HW":
747
+ sql = """
748
+ SELECT dtm from messages WHERE
749
+ verb in (' I', 'RP')
750
+ AND (src = ? OR dst = ?)
751
+ AND (ctx IN ('FC', 'FA', 'F9', 'FA') OR plk LIKE ?)
752
+ """
753
+ _ctx_qry = "%dhw_idx%"
754
+ # TODO add Children messages? self.ctl.dhw
755
+ else:
756
+ sql = """
757
+ SELECT dtm from messages WHERE
758
+ verb in (' I', 'RP')
759
+ AND (src = ? OR dst = ?)
760
+ AND ctx LIKE ?
761
+ """
762
+ _ctx_qry = f"%{self.id[_ID_SLICE + 1 :]}%"
763
+
764
+ _msg_dict = { # since 0.52.3 use ctx (context) instead of just the address
698
765
  m.code: m
699
766
  for m in self._gwy.msg_db.qry(
700
- sql, (self.id[:_SQL_SLICE], self.id[:_SQL_SLICE])
701
- ) # e.g. 01:123456_HW
767
+ sql, (self.id[:_ID_SLICE], self.id[:_ID_SLICE], _ctx_qry)
768
+ ) # e.g. 01:123456_HW, 01:123456_02 (Zone)
702
769
  }
703
770
  # if CTL, remove 3150, 3220 heat_demand, both are only stored on children
704
771
  # HACK
@@ -773,7 +840,7 @@ class _Discovery(_MessageDB):
773
840
  code: CODES_SCHEMA[code][SZ_NAME]
774
841
  for code in sorted(
775
842
  self._gwy.msg_db.get_rp_codes(
776
- (self.id[:_SQL_SLICE], self.id[:_SQL_SLICE])
843
+ (self.id[:_ID_SLICE], self.id[:_ID_SLICE])
777
844
  )
778
845
  )
779
846
  if self._is_not_deprecated_cmd(code)
@@ -805,7 +872,7 @@ class _Discovery(_MessageDB):
805
872
  AND (src = ? OR dst = ?)
806
873
  """
807
874
  for rec in self._gwy.msg_db.qry_field(
808
- sql, (self.id[:_SQL_SLICE], self.id[:_SQL_SLICE])
875
+ sql, (self.id[:_ID_SLICE], self.id[:_ID_SLICE])
809
876
  ):
810
877
  _LOGGER.debug("Fetched OT ctx from index: %s", rec[0])
811
878
  res.append(rec[0])
@@ -936,8 +1003,8 @@ class _Discovery(_MessageDB):
936
1003
  sql,
937
1004
  (
938
1005
  task[_SZ_COMMAND].code,
939
- self.tcs.id[:_ID_SLICE], # OK? not _SQL_SLICE?
940
- self.tcs.id[:_ID_SLICE], # OK? not _SQL_SLICE?
1006
+ self.tcs.id[:_ID_SLICE],
1007
+ self.tcs.id[:_ID_SLICE],
941
1008
  ),
942
1009
  )[0] # expect 1 Message in returned tuple
943
1010
  else: # TODO(eb) remove next Q1 2026
ramses_rf/gateway.py CHANGED
@@ -213,7 +213,7 @@ class Gateway(Engine):
213
213
  def _pause(self, *args: Any) -> None:
214
214
  """Pause the (unpaused) gateway (disables sending/discovery).
215
215
 
216
- There is the option to save other objects, as *args.
216
+ There is the option to save other objects, as `args`.
217
217
  """
218
218
  _LOGGER.debug("Gateway: Pausing engine...")
219
219
 
@@ -228,7 +228,7 @@ class Gateway(Engine):
228
228
  def _resume(self) -> tuple[Any]:
229
229
  """Resume the (paused) gateway (enables sending/discovery, if applicable).
230
230
 
231
- Will restore other objects, as *args.
231
+ Will restore other objects, as `args`.
232
232
  """
233
233
  args: tuple[Any]
234
234
 
ramses_rf/schemas.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """RAMSES RF - a RAMSES-II protocol decoder & analyser.
3
3
 
4
- Schema processor for upper layer.
4
+ :term:`Schema` processor for upper layer.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
ramses_rf/system/zones.py CHANGED
@@ -233,7 +233,7 @@ class DhwZone(ZoneSchedule): # CS92A
233
233
  # def eavesdrop_dhw_sensor(this: Message, *, prev: Message | None = None) -> None:
234
234
  # """Eavesdrop packets, or pairs of packets, to maintain the system state.
235
235
 
236
- # There are only 2 ways to to find a controller's DHW sensor:
236
+ # There are only 2 ways to find a controller's DHW sensor:
237
237
  # 1. The 10A0 RQ/RP *from/to a 07:* (1x/4h) - reliable
238
238
  # 2. Use sensor temp matching - non-deterministic
239
239
 
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.1"
3
+ __version__ = "0.52.3"
4
4
  VERSION = __version__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramses_rf
3
- Version: 0.52.1
3
+ Version: 0.52.3
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
@@ -4,52 +4,52 @@ ramses_cli/debug.py,sha256=vgR0lOHoYjWarN948dI617WZZGNuqHbeq6Tc16Da7b4,608
4
4
  ramses_cli/discovery.py,sha256=MWoahBnAAVzfK2S7EDLsY2WYqN_ZK9L-lktrj8_4cb0,12978
5
5
  ramses_cli/utils/cat_slow.py,sha256=AhUpM5gnegCitNKU-JGHn-DrRzSi-49ZR1Qw6lxe_t8,607
6
6
  ramses_cli/utils/convert.py,sha256=D_YiCyX5na9pgC-_NhBlW9N1dgRKUK-uLtLBfofjzZM,1804
7
- ramses_rf/__init__.py,sha256=VG3E9GHbtC6lx6E1DMQJeFitHnydMKJyPxQBethdrzg,1193
8
- ramses_rf/binding_fsm.py,sha256=uZAOl3i19KCXqqlaLJWkEqMMP7NJBhVPW3xTikQD1fY,25996
7
+ ramses_rf/__init__.py,sha256=vp2TyFGqc1fGQHsevhmaw0QEmSSCnZx7fqizKiEwHtw,1245
8
+ ramses_rf/binding_fsm.py,sha256=fuqvcc9YW-wr8SPH8zadpPqrHAvzl_eeWF-IBtlLppY,26632
9
9
  ramses_rf/const.py,sha256=L3z31CZ-xqno6oZp_h-67CB_5tDDqTwSWXsqRtsjMcs,5460
10
- ramses_rf/database.py,sha256=aQntZW-ARiJ8GFVeT5T4LLWMeshs_lw_tnNet_S146U,20785
11
- ramses_rf/dispatcher.py,sha256=UqilAoL6T85ZFPkcpvcfuidNbusONngQoCVWHV6vOzk,11277
12
- ramses_rf/entity_base.py,sha256=T3NCo41l6KDqip6ptLdYn2b7pmGit37NgAHoBvhT42s,56063
10
+ ramses_rf/database.py,sha256=rMfEJvapc3Zh6SFqjPjntuuaYynuZiAdYTw1pcXsJPE,20344
11
+ ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
12
+ ramses_rf/entity_base.py,sha256=uN8QGGv0cutCYTT-oyDkvzv4xpBEObWF4jo9ArtvbLE,57640
13
13
  ramses_rf/exceptions.py,sha256=mt_T7irqHSDKir6KLaf6oDglUIdrw0S40JbOrWJk5jc,3657
14
- ramses_rf/gateway.py,sha256=AEgVKoW1I3D8zYIkQfMnC6CN3TV1gxamfIaXRQ2IUSk,21216
14
+ ramses_rf/gateway.py,sha256=VYZqMppU_kDYhT3EUmqpHf0LuLXPMH7ASx9jylAopWE,21218
15
15
  ramses_rf/helpers.py,sha256=TNk_QkpIOB3alOp1sqnA9LOzi4fuDCeapNlW3zTzNas,4250
16
16
  ramses_rf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- ramses_rf/schemas.py,sha256=UhvRhV4nZ3kvrLLM3wwIguQUIjIgd_AKvp2wkTSpNEA,13468
18
- ramses_rf/version.py,sha256=X_vGsHP_Eb6ovijJi7zzwEWH5kud2r0V1sImKWLqKv4,125
17
+ ramses_rf/schemas.py,sha256=jvpx0hZiMhaogyxbxnrbxS7m2GekKK4DFfVTNs7h-WQ,13476
18
+ ramses_rf/version.py,sha256=E3rkZTOmbKgrnpOmzEdweYaquvnmc18LcdiXXdsnakM,125
19
19
  ramses_rf/device/__init__.py,sha256=sUbH5dhbYFXSoM_TPFRutpRutBRpup7_cQ9smPtDTy8,4858
20
20
  ramses_rf/device/base.py,sha256=Yx0LZwMEb49naY8FolZ8HEBFb6XCPQBTVN_TWyO2nKg,17777
21
- ramses_rf/device/heat.py,sha256=UmZuA-5czrfoClVEeUAPWgze5obWNQpYI7ZPQpVJB6s,54704
22
- ramses_rf/device/hvac.py,sha256=0iQt9yFXgapaaiLWimGYF1Mh2c_TR58JyaYvWU1nSdE,45605
21
+ ramses_rf/device/heat.py,sha256=tjORTbjxBKFzgPQbfpLuj74IlrVtOmj9GpzslK9j0A0,54569
22
+ ramses_rf/device/hvac.py,sha256=gdpVACUvtq6ERMI0mwRhqIJtKYEmybzJA2NeyN1ELrs,48431
23
23
  ramses_rf/system/__init__.py,sha256=uZLKio3gLlBzePa2aDQ1nxkcp1YXOGrn6iHTG8LiNIw,711
24
24
  ramses_rf/system/faultlog.py,sha256=GdGmVGT3137KsTlV_nhccgIFEmYu6DFsLTn4S-8JSok,12799
25
25
  ramses_rf/system/heat.py,sha256=3jaFEChU-HlWCRMY1y7u09s7AH4hT0pC63hnqwdmZOc,39223
26
26
  ramses_rf/system/schedule.py,sha256=Ts6tdZPTQLV5NkgwA73tPa5QUsnZNIIuYoKC-8VsXDk,18808
27
- ramses_rf/system/zones.py,sha256=9AH7ooN5QfiqvWuor2P1Dn8aILjQb2RWL9rWqDH1IjA,36075
28
- ramses_tx/__init__.py,sha256=qNMTe8hBkIuecvtCiekUB0pKdD8atb0SjWxVNVe3yhE,3538
27
+ ramses_rf/system/zones.py,sha256=YN_HAbeaa2YioUOjafEpp-0IHmIwmKNSK_77pPcjtns,36072
28
+ ramses_tx/__init__.py,sha256=sqnjM7pUGJDmec6igTtKViSB8FLX49B5gwhAmcY9ERY,3596
29
29
  ramses_tx/address.py,sha256=F5ZE-EbPNNom1fW9XXUILvD7DYSMBxNJvsHVliT5gjw,8452
30
- ramses_tx/command.py,sha256=r9dNaofjjOQXZSUrZjsNpvEukNn4rSGy0OLr2Dyd2TI,125129
30
+ ramses_tx/command.py,sha256=tqVECwd_QokEcRv2MSk7TUU4JSBzCZcJh1eQ0jIGgoY,125122
31
31
  ramses_tx/const.py,sha256=pnxq5upXvLUizv9Ye_I1llD9rAa3wddHgsSkc91AIUc,30300
32
32
  ramses_tx/exceptions.py,sha256=FJSU9YkvpKjs3yeTqUJX1o3TPFSe_B01gRGIh9b3PNc,2632
33
33
  ramses_tx/fingerprints.py,sha256=nfftA1E62HQnb-eLt2EqjEi_la0DAoT0wt-PtTMie0s,11974
34
34
  ramses_tx/frame.py,sha256=GzNsXr15YLeidJYGtk_xPqsZQh4ehDDlUCtT6rTDhT8,22046
35
- ramses_tx/gateway.py,sha256=8o9NyEAyIui3B6HA0R8o64u4U8NZFlNl0TQ6rhwMoCk,11369
35
+ ramses_tx/gateway.py,sha256=Fl6EqAUU2DnLOiA2_87sS7VPBEyxA1a_ICCmg55WMMA,11494
36
36
  ramses_tx/helpers.py,sha256=J4OCRckp3JshGQTvvqEskFjB1hPS7uA_opVsuIqmZds,32915
37
- ramses_tx/logger.py,sha256=qYbUoNPnPaFWKVsYvLG6uTVuPTdZ8HsMzBbGx0DpBqc,10177
37
+ ramses_tx/logger.py,sha256=1iKRHKUaqHqGd76CkE_6mCVR0sYODtxshRRwfY61fTk,10426
38
38
  ramses_tx/message.py,sha256=-moQ8v3HVlNSl-x3U0DDfDcj8WQ7vLqclMNxsohbmnw,13449
39
39
  ramses_tx/opentherm.py,sha256=58PXz9l5x8Ou6Fm3y-R_UnGHCYahoi2RKIDdYStUMzk,42378
40
40
  ramses_tx/packet.py,sha256=_qHiPFWpQpKueZOgf1jJ93Y09iZjo3LZWStLglVkXg4,7370
41
- ramses_tx/parsers.py,sha256=e6IwVEMLv2EL8gbZaM2s-qA3E2AN8dCwdIgfEbFo730,111029
41
+ ramses_tx/parsers.py,sha256=Z0ochrNyO2l8SAP0oJxN-tx2nJluEaNP_uJCIm_lsA8,111279
42
42
  ramses_tx/protocol.py,sha256=9R3aCzuiWEyXmugmB5tyR_RhBlgfnpUXj7AsMP9BzzU,28867
43
43
  ramses_tx/protocol_fsm.py,sha256=ZKtehCr_4TaDdfdlfidFLJaOVTYtaEq5h4tLqNIhb9s,26827
44
44
  ramses_tx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  ramses_tx/ramses.py,sha256=NG81GBNZlap-Gi9ac-r6OFE-KaHvXsgPDWy-I2Irr-4,53698
46
- ramses_tx/schemas.py,sha256=IYCDH0jp3446Gtl_378aBmWyXN90e-uYudfkZkOKR24,13147
47
- ramses_tx/transport.py,sha256=bGprlfuuwBgQ1bmBRSrcicuk7s-jVqyuKpZCfQ-sSpw,58469
46
+ ramses_tx/schemas.py,sha256=bqKW_V0bR6VbBD8ZQiBExNtVdXs0fryVKe3GEhupgIo,13424
47
+ ramses_tx/transport.py,sha256=oVClKNnCfHioIk5tF0TGmYspJhpggQ6EspOYeyBBR4g,58841
48
48
  ramses_tx/typed_dicts.py,sha256=w-0V5t2Q3GiNUOrRAWiW9GtSwbta_7luME6GfIb1zhI,10869
49
49
  ramses_tx/typing.py,sha256=eF2SlPWhNhEFQj6WX2AhTXiyRQVXYnFutiepllYl2rI,5042
50
- ramses_tx/version.py,sha256=Wf50ZhDvYDcZoyYmD6Yu8apfRhpvbfMyTBj_GYjoYbw,123
51
- ramses_rf-0.52.1.dist-info/METADATA,sha256=7mrN8yPSMpAdK4-lYvWCmhKyRLrJgr2WQE4px21H7t0,4000
52
- ramses_rf-0.52.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- ramses_rf-0.52.1.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
54
- ramses_rf-0.52.1.dist-info/licenses/LICENSE,sha256=-Kc35W7l1UkdiQ4314_yVWv7vDDrg7IrJfMLUiq6Nfs,1074
55
- ramses_rf-0.52.1.dist-info/RECORD,,
50
+ ramses_tx/version.py,sha256=OmZAmWYXnodOMX2FqbBZjLsKSaBVpA4Y37368OIox1o,123
51
+ ramses_rf-0.52.3.dist-info/METADATA,sha256=u8gl35WcjLN5rO1P_BTfchK23MayeJLDQxFEhHOM-v4,4000
52
+ ramses_rf-0.52.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
+ ramses_rf-0.52.3.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
54
+ ramses_rf-0.52.3.dist-info/licenses/LICENSE,sha256=-Kc35W7l1UkdiQ4314_yVWv7vDDrg7IrJfMLUiq6Nfs,1074
55
+ ramses_rf-0.52.3.dist-info/RECORD,,
ramses_tx/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- """RAMSES RF - a RAMSES-II protocol decoder & analyser."""
2
+ """RAMSES RF - a RAMSES-II protocol decoder & analyser.
3
+ `ramses_tx` takes care of the RF protocol (lower) layer.
4
+ """
3
5
 
4
6
  from __future__ import annotations
5
7
 
ramses_tx/command.py CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
- """RAMSES RF - a RAMSES-II protocol decoder & analyser.
2
+ """
3
+ RAMSES RF - a RAMSES-II protocol decoder & analyser.
3
4
 
4
5
  This module provides the `Command` class for constructing and managing RAMSES-II protocol
5
6
  commands (packets) that are to be sent to HVAC devices. It includes methods for creating
@@ -1394,7 +1395,7 @@ class Command(Frame):
1394
1395
 
1395
1396
  :param ctl_id: The device ID of the DHW controller
1396
1397
  :type ctl_id: DeviceIdT | str
1397
- :param **kwargs: Additional parameters (currently only 'dhw_idx' is supported)
1398
+ :param kwargs: Additional parameters (currently only 'dhw_idx' is supported)
1398
1399
  :key dhw_idx: The DHW circuit index (0 or 1, defaults to 0 for single-DHW systems)
1399
1400
  :type dhw_idx: int, optional
1400
1401
  :return: A Command object for the RQ|1F41 message
@@ -1438,7 +1439,7 @@ class Command(Frame):
1438
1439
  :type until: datetime | str | None
1439
1440
  :param duration: Duration in seconds for temporary mode (alternative to 'until')
1440
1441
  :type duration: int | None
1441
- :param **kwargs: Additional parameters (currently only 'dhw_idx' is supported)
1442
+ :param kwargs: Additional parameters (currently only 'dhw_idx' is supported)
1442
1443
  :key dhw_idx: The DHW circuit index (0 or 1, defaults to 0 for single-DHW systems)
1443
1444
  :type dhw_idx: int, optional
1444
1445
  :return: A Command object for the W|1F41 message
@@ -1501,7 +1502,7 @@ class Command(Frame):
1501
1502
  :type codes: Code | Iterable[Code] | None
1502
1503
  :param dst_id: Optional destination device ID (defaults to broadcast)
1503
1504
  :type dst_id: DeviceIdT | str | None
1504
- :param **kwargs: Additional parameters
1505
+ :param kwargs: Additional parameters
1505
1506
  :key oem_code: OEM code for bind offers (only used with I-type messages)
1506
1507
  :type oem_code: str, optional
1507
1508
  :return: A Command object for the bind operation
@@ -2531,7 +2532,7 @@ class Command(Frame):
2531
2532
  :type supply_flow: float | None
2532
2533
  :param exhaust_flow: Current exhaust air flow rate (if available)
2533
2534
  :type exhaust_flow: float | None
2534
- :param **kwargs: Additional parameters (reserved for future use)
2535
+ :param kwargs: Additional parameters (reserved for future use)
2535
2536
  :return: A configured Command object for the HVAC fan status update
2536
2537
  :rtype: Command
2537
2538
 
ramses_tx/gateway.py CHANGED
@@ -34,6 +34,7 @@ from .schemas import (
34
34
  SZ_DISABLE_QOS,
35
35
  SZ_DISABLE_SENDING,
36
36
  SZ_ENFORCE_KNOWN_LIST,
37
+ SZ_LOG_ALL_MQTT,
37
38
  SZ_PACKET_LOG,
38
39
  SZ_PORT_CONFIG,
39
40
  SZ_PORT_NAME,
@@ -114,6 +115,7 @@ class Engine:
114
115
  self._exclude,
115
116
  )
116
117
  self._sqlite_index = kwargs.pop(SZ_SQLITE_INDEX, False) # default True?
118
+ self._log_all_mqtt = kwargs.pop(SZ_LOG_ALL_MQTT, False)
117
119
  self._kwargs: dict[str, Any] = kwargs # HACK
118
120
 
119
121
  self._engine_lock = Lock() # FIXME: threading lock, or asyncio lock?
@@ -197,6 +199,7 @@ class Engine:
197
199
  self._protocol,
198
200
  disable_sending=self._disable_sending,
199
201
  loop=self._loop,
202
+ log_all=self._log_all_mqtt,
200
203
  **pkt_source,
201
204
  **self._kwargs, # HACK: odd/misc params, e.g. comms_params
202
205
  )
ramses_tx/logger.py CHANGED
@@ -162,6 +162,14 @@ class StdOutFilter(logging.Filter): # record.levelno < logging.WARNING
162
162
  return record.levelno < logging.WARNING
163
163
 
164
164
 
165
+ class BlockMqttFilter(logging.Filter):
166
+ """Block mqtt traffic"""
167
+
168
+ def filter(self, record: logging.LogRecord) -> bool:
169
+ """Return True if the record is to be processed."""
170
+ return not record.getMessage().startswith("mq Rx: ")
171
+
172
+
165
173
  class TimedRotatingFileHandler(_TimedRotatingFileHandler):
166
174
  def __init__(self, *args: Any, **kwargs: Any) -> None:
167
175
  super().__init__(*args, **kwargs)