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/__init__.py +1 -0
- ramses_rf/binding_fsm.py +18 -4
- ramses_rf/database.py +57 -39
- ramses_rf/device/heat.py +4 -6
- ramses_rf/device/hvac.py +116 -56
- ramses_rf/dispatcher.py +9 -4
- ramses_rf/entity_base.py +133 -66
- ramses_rf/gateway.py +2 -2
- ramses_rf/schemas.py +1 -1
- ramses_rf/system/zones.py +1 -1
- ramses_rf/version.py +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.3.dist-info}/METADATA +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.3.dist-info}/RECORD +24 -24
- ramses_tx/__init__.py +3 -1
- ramses_tx/command.py +6 -5
- ramses_tx/gateway.py +3 -0
- ramses_tx/logger.py +8 -0
- ramses_tx/parsers.py +41 -37
- ramses_tx/schemas.py +28 -15
- ramses_tx/transport.py +45 -25
- ramses_tx/version.py +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.3.dist-info}/WHEEL +0 -0
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.3.dist-info}/entry_points.txt +0 -0
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.3.dist-info}/licenses/LICENSE +0 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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[:
|
|
562
|
+
sql, (self.id[:_ID_SLICE], self.id[:_ID_SLICE], _ctx_qry)
|
|
533
563
|
):
|
|
534
|
-
_LOGGER.debug(
|
|
535
|
-
|
|
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.
|
|
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
|
|
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
|
|
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,
|
|
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(
|
|
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[:
|
|
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
|
-
|
|
737
|
+
# if self.id[:3] == "18:": # HGI, confirm this is correct, tests suggest so
|
|
738
|
+
# return {}
|
|
686
739
|
|
|
687
|
-
|
|
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
|
-
|
|
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[:
|
|
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[:
|
|
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[:
|
|
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],
|
|
940
|
-
self.tcs.id[:_ID_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
|
|
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
|
|
231
|
+
Will restore other objects, as `args`.
|
|
232
232
|
"""
|
|
233
233
|
args: tuple[Any]
|
|
234
234
|
|
ramses_rf/schemas.py
CHANGED
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
|
|
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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ramses_rf
|
|
3
|
-
Version: 0.52.
|
|
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=
|
|
8
|
-
ramses_rf/binding_fsm.py,sha256=
|
|
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=
|
|
11
|
-
ramses_rf/dispatcher.py,sha256=
|
|
12
|
-
ramses_rf/entity_base.py,sha256=
|
|
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=
|
|
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=
|
|
18
|
-
ramses_rf/version.py,sha256=
|
|
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=
|
|
22
|
-
ramses_rf/device/hvac.py,sha256=
|
|
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=
|
|
28
|
-
ramses_tx/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
47
|
-
ramses_tx/transport.py,sha256=
|
|
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=
|
|
51
|
-
ramses_rf-0.52.
|
|
52
|
-
ramses_rf-0.52.
|
|
53
|
-
ramses_rf-0.52.
|
|
54
|
-
ramses_rf-0.52.
|
|
55
|
-
ramses_rf-0.52.
|
|
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
ramses_tx/command.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|