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 CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """RAMSES RF - a RAMSES-II protocol decoder & analyser.
3
+ `ramses_rf` takes care of the device (upper) layer.
3
4
 
4
5
  Works with (amongst others):
5
6
  - evohome (up to 12 zones)
ramses_rf/binding_fsm.py CHANGED
@@ -178,6 +178,8 @@ class BindContextBase:
178
178
  self, state: type[BindStateBase], result: asyncio.Future[Message] | None = None
179
179
  ) -> None:
180
180
  """Transition the State of the Context, and process the result, if any."""
181
+ # Ensure prev_state is always available, not only during debugging
182
+ prev_state = self._state
181
183
 
182
184
  # if False and result:
183
185
  # try:
@@ -185,10 +187,6 @@ class BindContextBase:
185
187
  # except exc.BindingError as err:
186
188
  # self._fut.set_result(err)
187
189
 
188
- if _DBG_MAINTAIN_STATE_CHAIN: # HACK for debugging
189
- # if prev_state in (None, )
190
- prev_state = self._state
191
-
192
190
  self._state = state(self)
193
191
  if not self.is_binding:
194
192
  self._is_respondent = None
@@ -197,6 +195,14 @@ class BindContextBase:
197
195
  elif state is SuppSendOfferWaitForAccept:
198
196
  self._is_respondent = False
199
197
 
198
+ # Log binding completion transitions
199
+ if isinstance(
200
+ self._state, (RespHasBoundAsRespondent, SuppHasBoundAsSupplicant)
201
+ ):
202
+ _LOGGER.info(
203
+ f"{self._dev.id}: Binding process completed: {type(prev_state).__name__} -> {state.__name__} (role: {self.role})"
204
+ )
205
+
200
206
  if _DBG_MAINTAIN_STATE_CHAIN: # HACK for debugging
201
207
  setattr(self._state, "_prev_state", prev_state) # noqa: B010
202
208
 
@@ -663,6 +669,10 @@ class RespHasBoundAsRespondent(BindStateBase):
663
669
 
664
670
  _attr_role = BindRole.IS_DORMANT
665
671
 
672
+ def __init__(self, context: BindContextBase) -> None:
673
+ super().__init__(context)
674
+ _LOGGER.info(f"{context._dev.id}: Binding completed as respondent")
675
+
666
676
 
667
677
  class RespIsWaitingForAddenda(_DevIsWaitingForMsg, BindStateBase):
668
678
  """Respondent has received a Confirm & is waiting for an Addenda."""
@@ -715,6 +725,10 @@ class SuppHasBoundAsSupplicant(BindStateBase):
715
725
 
716
726
  _attr_role = BindRole.IS_DORMANT
717
727
 
728
+ def __init__(self, context: BindContextBase) -> None:
729
+ super().__init__(context)
730
+ _LOGGER.info(f"{context._dev.id}: Binding completed as supplicant")
731
+
718
732
 
719
733
  class SuppIsReadyToSendAddenda(
720
734
  _DevIsReadyToSendCmd, BindStateBase
ramses_rf/database.py CHANGED
@@ -1,5 +1,25 @@
1
1
  #!/usr/bin/env python3
2
- """RAMSES RF - Message database and index."""
2
+ """
3
+ RAMSES RF - Message database and index.
4
+
5
+ .. table:: Database Query Methods[^1][#fn1]
6
+ :widths: auto
7
+
8
+ ===== ============ =========== ========== ==== ========================
9
+ ix method name args returns uses used by
10
+ ===== ============ =========== ========== ==== ========================
11
+ i1 get Msg, kwargs tuple(Msg) i3
12
+ i2 contains kwargs bool i4
13
+ i3 _select_from kwargs tuple(Msg) i4
14
+ i4 qry_dtms kwargs list(dtm)
15
+ i5 qry sql, kwargs tuple(Msg) _msgs()
16
+ i6 qry_field sql, kwargs tuple(fld) e4, e5
17
+ i7 get_rp_codes src, dst list(Code) Discovery-supported_cmds
18
+ ===== ============ =========== ========== ==== ========================
19
+
20
+ [#fn1] A word of explanation.
21
+ [^1]: ex = entity_base.py query methods
22
+ """
3
23
 
4
24
  from __future__ import annotations
5
25
 
@@ -62,9 +82,10 @@ def payload_keys(parsed_payload: list[dict] | dict) -> str: # type: ignore[type
62
82
 
63
83
 
64
84
  class MessageIndex:
65
- """A simple in-memory SQLite3 database for indexing RF messages.
66
- Index holds the latest message to & from all devices by header
67
- (example of a hdr: 000C|RP|01:223036|0208)."""
85
+ """A central in-memory SQLite3 database for indexing RF messages.
86
+ Index holds all the latest messages to & from all devices by `dtm`
87
+ (timestamp) and `hdr` header
88
+ (example of a hdr: ``000C|RP|01:223036|0208``)."""
68
89
 
69
90
  _housekeeping_task: asyncio.Task[None]
70
91
 
@@ -72,7 +93,8 @@ class MessageIndex:
72
93
  """Instantiate a message database/index."""
73
94
 
74
95
  self.maintain = maintain
75
- self._msgs: MsgDdT = OrderedDict() # stores all messages for retrieval. Filled & cleaned up in housekeeping_loop.
96
+ self._msgs: MsgDdT = OrderedDict() # stores all messages for retrieval.
97
+ # Filled & cleaned up in housekeeping_loop.
76
98
 
77
99
  # Connect to a SQLite DB in memory
78
100
  self._cx = sqlite3.connect(
@@ -122,16 +144,17 @@ class MessageIndex:
122
144
  def _setup_db_schema(self) -> None:
123
145
  """Set up the message database schema.
124
146
 
125
- messages TABLE Fields:
126
-
127
- - dtm message timestamp
128
- - verb " I", "RQ" etc.
129
- - src message origin address
130
- - dst message destination address
131
- - code packet code aka command class e.g. _0005, _31DA
132
- - ctx message context, created from payload as index + extra markers (Heat)
133
- - hdr packet header e.g. 000C|RP|01:223036|0208 (see: src/ramses_tx/frame.py)
134
- - plk the keys stored in the parsed payload, separated by the | char
147
+ .. note::
148
+ messages TABLE Fields:
149
+
150
+ - dtm message timestamp
151
+ - verb " I", "RQ" etc.
152
+ - src message origin address
153
+ - dst message destination address
154
+ - code packet code aka command class e.g. _0005, _31DA
155
+ - ctx message context, created from payload as index + extra markers (Heat)
156
+ - hdr packet header e.g. 000C|RP|01:223036|0208 (see: src/ramses_tx/frame.py)
157
+ - plk the keys stored in the parsed payload, separated by the | char
135
158
  """
136
159
 
137
160
  self._cu.execute(
@@ -160,7 +183,7 @@ class MessageIndex:
160
183
 
161
184
  async def _housekeeping_loop(self) -> None:
162
185
  """Periodically remove stale messages from the index,
163
- unless self.maintain is False."""
186
+ unless `self.maintain` is False."""
164
187
 
165
188
  async def housekeeping(dt_now: dt, _cutoff: td = td(days=1)) -> None:
166
189
  """
@@ -196,6 +219,7 @@ class MessageIndex:
196
219
  """
197
220
  Add a single message to the MessageIndex.
198
221
  Logs a warning if there is a duplicate dtm.
222
+
199
223
  :returns: any message that was removed because it had the same header
200
224
  """
201
225
  # TODO: eventually, may be better to use SqlAlchemy
@@ -239,7 +263,11 @@ class MessageIndex:
239
263
 
240
264
  def add_record(self, src: str, code: str = "", verb: str = "") -> None:
241
265
  """
242
- Add a single record to the MessageIndex with timestamp now() and no Message contents.
266
+ Add a single record to the MessageIndex with timestamp `now()` and no Message contents.
267
+
268
+ :param src: device id to use as source address
269
+ :param code: device id to use as destination address (can be identical)
270
+ :param verb: two letter verb str to use
243
271
  """
244
272
  # Used by OtbGateway init, via entity_base.py
245
273
  dtm: DtmStrT = dt.strftime(dt.now(), "%Y-%m-%dT%H:%M:%S") # type: ignore[assignment]
@@ -274,6 +302,7 @@ class MessageIndex:
274
302
  def _insert_into(self, msg: Message) -> Message | None:
275
303
  """
276
304
  Insert a message into the index.
305
+
277
306
  :returns: any message replaced (by same hdr)
278
307
  """
279
308
  assert msg._pkt._hdr is not None, "Skipping: Packet has no hdr: {msg._pkt}"
@@ -305,7 +334,7 @@ class MessageIndex:
305
334
  payload_keys(msg.payload),
306
335
  ),
307
336
  )
308
- _LOGGER.debug(f"Added {msg} to gwy.msg_db")
337
+ # _LOGGER.debug(f"Added {msg} to gwy.msg_db")
309
338
 
310
339
  return _old_msgs[0] if _old_msgs else None
311
340
 
@@ -348,6 +377,7 @@ class MessageIndex:
348
377
 
349
378
  def _delete_from(self, **kwargs: bool | dt | str) -> tuple[Message, ...]:
350
379
  """Remove message(s) from the index.
380
+
351
381
  :returns: any messages that were removed"""
352
382
 
353
383
  msgs = self._select_from(**kwargs)
@@ -359,32 +389,14 @@ class MessageIndex:
359
389
 
360
390
  return msgs
361
391
 
362
- # MessageIndex msg_db query methods > copy to docs/source/ramses_rf.rst
363
- # (ex = entity_base.py query methods
364
- #
365
- # +----+--------------+-------------+------------+------+--------------------------+
366
- # | ix |method name | args | returns | uses | used by |
367
- # +====+==============+=============+============+======+==========================+
368
- # | i1 | get | Msg/kwargs | tuple[Msg] | i3 | |
369
- # +----+--------------+-------------+------------+------+--------------------------+
370
- # | i2 | contains | kwargs | bool | i4 | |
371
- # +----+--------------+-------------+------------+------+--------------------------+
372
- # | i3 | _select_from | kwargs | tuple[Msg] | i4 | |
373
- # +----+--------------+-------------+------------+------+--------------------------+
374
- # | i4 | qry_dtms | kwargs | list(dtm) | | |
375
- # +----+--------------+-------------+------------+------+--------------------------+
376
- # | i5 | qry | sql, kwargs | tuple[Msg] | | _msgs() |
377
- # +----+--------------+-------------+------------+------+--------------------------+
378
- # | i6 | qry_field | sql, kwargs | tuple[fld] | | e4, e5 |
379
- # +----+--------------+-------------+------------+------+--------------------------+
380
- # | i7 | get_rp_codes | src, dst | list[Code] | | Discovery#supported_cmds |
381
- # +----+--------------+-------------+------------+------+--------------------------+
392
+ # MessageIndex msg_db query methods
382
393
 
383
394
  def get(
384
395
  self, msg: Message | None = None, **kwargs: bool | dt | str
385
396
  ) -> tuple[Message, ...]:
386
397
  """
387
398
  Public method to get a set of message(s) from the index.
399
+
388
400
  :param msg: Message to return, by dtm (expect a single result as dtm is unique key)
389
401
  :param kwargs: data table field names and criteria, e.g. (hdr=...)
390
402
  :return: tuple of matching Messages
@@ -401,6 +413,7 @@ class MessageIndex:
401
413
  def contains(self, **kwargs: bool | dt | str) -> bool:
402
414
  """
403
415
  Check if the MessageIndex contains at least 1 record that matches the provided fields.
416
+
404
417
  :param kwargs: (exact) SQLite table field_name: required_value pairs
405
418
  :return: True if at least one message fitting the given conditions is present, False when qry returned empty
406
419
  """
@@ -410,6 +423,7 @@ class MessageIndex:
410
423
  def _select_from(self, **kwargs: bool | dt | str) -> tuple[Message, ...]:
411
424
  """
412
425
  Select message(s) using the MessageIndex.
426
+
413
427
  :param kwargs: (exact) SQLite table field_name: required_value pairs
414
428
  :returns: a tuple of qualifying messages
415
429
  """
@@ -421,7 +435,8 @@ class MessageIndex:
421
435
 
422
436
  def qry_dtms(self, **kwargs: bool | dt | str) -> list[Any]:
423
437
  """
424
- Select from the ImageIndex a list of dtms that match the provided arguments.
438
+ Select from the MessageIndex a list of dtms that match the provided arguments.
439
+
425
440
  :param kwargs: data table field names and criteria
426
441
  :return: list of unformatted dtms that match, useful for msg lookup, or an empty list if 0 matches
427
442
  """
@@ -444,6 +459,7 @@ class MessageIndex:
444
459
  def qry(self, sql: str, parameters: tuple[str, ...]) -> tuple[Message, ...]:
445
460
  """
446
461
  Get a tuple of messages from _msgs using the index, given sql and parameters.
462
+
447
463
  :param sql: a bespoke SQL query SELECT string that should return dtm as first field
448
464
  :param parameters: tuple of kwargs with the selection filter
449
465
  :return: a tuple of qualifying messages
@@ -474,6 +490,7 @@ class MessageIndex:
474
490
  def get_rp_codes(self, parameters: tuple[str, ...]) -> list[Code]:
475
491
  """
476
492
  Get a list of Codes from the index, given parameters.
493
+
477
494
  :param parameters: tuple of additional kwargs
478
495
  :return: list of Code: value pairs
479
496
  """
@@ -499,6 +516,7 @@ class MessageIndex:
499
516
  ) -> list[tuple[dt | str, str]]:
500
517
  """
501
518
  Get a list of fields from the index, given select sql and parameters.
519
+
502
520
  :param sql: a bespoke SQL query SELECT string
503
521
  :param parameters: tuple of additional kwargs
504
522
  :return: list of key: value pairs as defined in sql
ramses_rf/device/heat.py CHANGED
@@ -120,7 +120,7 @@ _LOGGER = logging.getLogger(__name__)
120
120
 
121
121
  class Actuator(DeviceHeat): # 3EF0, 3EF1 (for 10:/13:)
122
122
  # .I --- 13:109598 --:------ 13:109598 3EF0 003 00C8FF # event-driven, 00/C8
123
- # RP --- 13:109598 18:002563 --:------ 0008 002 00C8 # 00/C8, as abobe
123
+ # RP --- 13:109598 18:002563 --:------ 0008 002 00C8 # 00/C8, as above
124
124
  # RP --- 13:109598 18:002563 --:------ 3EF1 007 0000BF-00BFC8FF # 00/C8, as above
125
125
 
126
126
  # RP --- 10:048122 18:140805 --:------ 3EF1 007 007FFF-003C2A10 # 10:s only RP, always 7FFF
@@ -668,11 +668,8 @@ class OtbGateway(Actuator, HeatDemand): # OTB (10): 3220 (22D9, others)
668
668
  self._child_id = FC # NOTE: domain_id
669
669
 
670
670
  # TODO(eb): cleanup
671
- # should fix src/ramses_rf/database.py _add_record try/except when activating next line
672
671
  if self._gwy.msg_db:
673
- self._add_record(
674
- address=self.addr, code=Code._3220, verb="RP"
675
- ) # << essential?
672
+ self._add_record(address=self.addr, code=Code._3220, verb="RP")
676
673
  # adds a "sim" RP opentherm_msg to the SQLite MessageIndex with code _3220
677
674
  # causes exc when fetching ALL, when no "real" msg was added to _msgs_. We skip those.
678
675
  else:
@@ -1263,6 +1260,7 @@ class BdrSwitch(Actuator, RelayDemand): # BDR (13):
1263
1260
  """The BDR class, such as a BDR91.
1264
1261
 
1265
1262
  BDR91s can be used in six distinct modes, including:
1263
+
1266
1264
  - x2 boiler controller (FC/TPI): either traditional, or newer heat pump-aware
1267
1265
  - x1 electric heat zones (0x/ELE)
1268
1266
  - x1 zone valve zones (0x/VAL)
@@ -1410,7 +1408,7 @@ class UfhCircuit(Child, Entity): # FIXME
1410
1408
  def __init__(self, ufc: UfhController, ufh_idx: str) -> None:
1411
1409
  super().__init__(ufc._gwy)
1412
1410
 
1413
- # FIXME: ZZZ entities must know their parent device ID and their own idx
1411
+ # FIXME: gwy.msg_db entities must know their parent device ID and their own idx
1414
1412
  self._z_id = ufc.id
1415
1413
  self._z_idx = ufh_idx
1416
1414
 
ramses_rf/device/hvac.py CHANGED
@@ -728,12 +728,15 @@ class HvacVentilator(FilterChange): # FAN: RP/31DA, I/31D[9A], 2411
728
728
  In HomeAssistant, ramses_cc, you can set a bound device in the device configuration.
729
729
 
730
730
  System schema and known devices example:
731
- "32:153289":
732
- bound: "37:168270"
733
- class: FAN
734
- "37:168270":
735
- class: REM
736
- faked: true
731
+
732
+ .. code-block::
733
+
734
+ "32:153289":
735
+ bound: "37:168270"
736
+ class: FAN
737
+ "37:168270":
738
+ class: REM
739
+ faked: true
737
740
 
738
741
  :param device_id: The unique identifier of the device to bind
739
742
  :type device_id: str
@@ -1142,7 +1145,7 @@ def class_dev_hvac(
1142
1145
  ) -> type[DeviceHvac]:
1143
1146
  """Return a device class, but only if the device must be from the HVAC group.
1144
1147
 
1145
- May return a base clase, DeviceHvac, which will need promotion.
1148
+ May return a base class, `DeviceHvac`, which will need promotion.
1146
1149
  """
1147
1150
 
1148
1151
  if not eavesdrop:
@@ -1206,54 +1209,111 @@ _REMOTES = {
1206
1209
  # see: https://github.com/arjenhiemstra/ithowifi/blob/master/software/NRG_itho_wifi/src/IthoPacket.h
1207
1210
 
1208
1211
  """
1209
- # CVE/HRU remote (536-0124) [RFT W: 3 modes, timer]
1210
- "away": (Code._22F1, 00, 01|04"), # how to invoke?
1211
- "low": (Code._22F1, 00, 02|04"), # aka eco
1212
- "medium": (Code._22F1, 00, 03|04"), # aka auto (with sensors) - is that only for 63?
1213
- "high": (Code._22F1, 00, 04|04"), # aka full
1214
-
1215
- "timer_1": (Code._22F3, 00, 00|0A"), # 10 minutes full speed
1216
- "timer_2": (Code._22F3, 00, 00|14"), # 20 minutes full speed
1217
- "timer_3": (Code._22F3, 00, 00|1E"), # 30 minutes full speed
1218
-
1219
- # RFT-AUTO (536-0150) [RFT CAR: 2 modes, auto, timer]: idx = 63, essentially same as above, but also...
1220
- "auto_night": (Code._22F8, 63, 02|03"), # additional - press auto x2
1221
-
1222
- # RFT-RV (04-00046), RFT-CO2 (04-00045) - sensors with control
1223
- "medium": (Code._22F1, 00, 03|07"), 1=away, 2=low?
1224
- "auto": (Code._22F1, 00, 05|07"), 4=high
1225
- "auto_night": (Code._22F1, 00, 0B|0B"),
1226
-
1227
- "timer_1": (Code._22F3, 00, 00|0A, 00|00, 0000"), # 10 minutes
1228
- "timer_2": (Code._22F3, 00, 00|14, 00|00, 0000"), # 20 minutes
1229
- "timer_3": (Code._22F3, 00, 00|1E, 00|00, 0000"), # 30 minutes
1230
-
1231
- # RFT-PIR (545-7550) - presence sensor
1232
-
1233
- # RFT_DF: DemandFlow remote (536-0146)
1234
- "timer_1": (Code._22F3, 00, 42|03, 03|03"), # 0b01-000-010 = 3 hrs, back to last mode
1235
- "timer_2": (Code._22F3, 00, 42|06, 03|03"), # 0b01-000-010 = 6 hrs, back to last mode
1236
- "timer_3": (Code._22F3, 00, 42|09, 03|03"), # 0b01-000-010 = 9 hrs, back to last mode
1237
- "cook_30": (Code._22F3, 00, 02|1E, 02|03"), # 30 mins (press 1x)
1238
- "cook_60": (Code._22F3, 00, 02|3C, 02|03"), # 60 mins (press 2x)
1239
-
1240
- "low": (Code._22F8, 00, 01|02"), # ?eco co2 <= 1200 ppm?
1241
- "high": (Code._22F8, 00, 02|02"), # ?comfort co2 <= 1000 ppm?
1242
-
1243
- # Join commands:
1244
- "CVERFT": (Code._1FC9, 00, Code._22F1, 0x000000, 01, Code._10E0, 0x000000"), # CVE/HRU remote (536-0124)
1245
- "AUTORFT": (Code._1FC9, 63, Code._22F8, 0x000000, 01, Code._10E0, 0x000000"), # AUTO RFT (536-0150)
1246
- "DF": (Code._1FC9, 00, Code._22F8, 0x000000, 00, Code._10E0, 0x000000"), # DemandFlow remote (536-0146)
1247
- "RV": (Code._1FC9, 00, Code._12A0, 0x000000, 01, Code._10E0, 0x000000, 00, Code._31E0, 0x000000, 00, Code._1FC9, 0x000000"), # RFT-RV (04-00046)
1248
- "CO2": (Code._1FC9, 00, Code._1298, 0x000000, 00, Code._2E10, 0x000000, 01, Code._10E0, 0x000000, 00, Code._31E0, 0x000000, 00, Code._1FC9, 0x000000"), # RFT-CO2 (04-00045)
1249
-
1250
- # Leave commands:
1251
- "Others": (Code._1FC9, 00, Code._1FC9, 0x000000"), # standard leave command
1252
- "AUTORFT": (Code._1FC9, 63, Code._1FC9, 0x000000"), # leave command of AUTO RFT (536-0150)
1253
-
1254
- # RQ 0x00
1255
- # I_ 0x01
1256
- # W_ 0x02
1257
- # RP 0x03
1212
+ Itho Remote (model) enums.
1213
+
1214
+ CVE/HRU remote (536-0124) RFT W: 3 modes, timer
1215
+ -------------------------------------------------
1216
+
1217
+ .. table:: 536-0124
1218
+ :widths: auto
1219
+
1220
+ =========== ========================= ================================================
1221
+ "away": (Code._22F1, 00, 01|04"), how to invoke?
1222
+ "low": (Code._22F1, 00, 02|04"), aka eco
1223
+ "medium": (Code._22F1, 00, 03|04"), aka auto (with sensors) - is that only for 63?
1224
+ "high": (Code._22F1, 00, 04|04"), aka full
1225
+
1226
+ "timer_1": (Code._22F3, 00, 00|0A"), 10 minutes full speed
1227
+ "timer_2": (Code._22F3, 00, 00|14"), 20 minutes full speed
1228
+ "timer_3": (Code._22F3, 00, 00|1E"), 30 minutes full speed
1229
+ =========== ========================= ================================================
1230
+
1231
+ RFT-AUTO (536-0150) RFT CAR: 2 modes, auto, timer: idx = 63, essentially same as above, but also...
1232
+ -----------------------------------------------------------------------------------------------------
1233
+
1234
+ .. table:: 536-0150
1235
+ :widths: auto
1236
+
1237
+ ============= ========================= ================================================
1238
+ "auto_night": (Code._22F8, 63, 02|03"), additional - press auto x2
1239
+ ============= ========================= ================================================
1240
+
1241
+ RFT-RV (04-00046), RFT-CO2 (04-00045) - sensors with control
1242
+ ------------------------------------------------------------
1243
+
1244
+ .. table:: 04-00046
1245
+ :widths: auto
1246
+
1247
+ ============== ======================================== =============
1248
+ "medium": (Code._22F1, 00, 03|07"), 1=away, 2=low?
1249
+ "auto": (Code._22F1, 00, 05|07"), 4=high
1250
+ "auto_night": (Code._22F1, 00, 0B|0B"),
1251
+
1252
+ "timer_1": (Code._22F3, 00, 00|0A, 00|00, 0000"), 10 minutes
1253
+ "timer_2": (Code._22F3, 00, 00|14, 00|00, 0000"), 20 minutes
1254
+ "timer_3": (Code._22F3, 00, 00|1E, 00|00, 0000"), 30 minutes
1255
+ ============== ======================================== =============
1256
+
1257
+ RFT-PIR (545-7550) - presence sensor
1258
+ ------------------------------------
1259
+
1260
+ RFT_DF: DemandFlow remote (536-0146)
1261
+ ------------------------------------
1262
+
1263
+ .. table:: 536-0146
1264
+ :widths: auto
1265
+
1266
+ =========== ================================ =========================================
1267
+ "timer_1": (Code._22F3, 00, 42|03, 03|03"), 0b01-000-010 = 3 hrs, back to last mode
1268
+ "timer_2": (Code._22F3, 00, 42|06, 03|03"), 0b01-000-010 = 6 hrs, back to last mode
1269
+ "timer_3": (Code._22F3, 00, 42|09, 03|03"), 0b01-000-010 = 9 hrs, back to last mode
1270
+ "cook_30": (Code._22F3, 00, 02|1E, 02|03"), 30 mins (press 1x)
1271
+ "cook_60": (Code._22F3, 00, 02|3C, 02|03"), 60 mins (press 2x)
1272
+
1273
+ "low": (Code._22F8, 00, 01|02"), ?eco co2 <= 1200 ppm?
1274
+ "high": (Code._22F8, 00, 02|02"), ?comfort co2 <= 1000 ppm?
1275
+ =========== ================================ =========================================
1276
+
1277
+
1278
+ Join commands:
1279
+ --------------
1280
+
1281
+ .. table:: join per accessory type
1282
+ :widths: auto
1283
+
1284
+ ========== ================= ===================== ========================= ========================== ========================= ========================== ================= ==========
1285
+ type set 1 set 2 set 3 set 4 set 5 set 6 description art #
1286
+ ========== ================= ===================== ========================= ========================== ========================= ========================== ================= ==========
1287
+ "CVERFT": (Code._1FC9, 00, Code._22F1, 0x000000, 01, Code._10E0, 0x000000") CVE/HRU remote (536-0124)
1288
+ "AUTORFT": (Code._1FC9, 63, Code._22F8, 0x000000, 01, Code._10E0, 0x000000") AUTO RFT (536-0150)
1289
+ "DF": (Code._1FC9, 00, Code._22F8, 0x000000, 00, Code._10E0, 0x000000") DemandFlow remote (536-0146)
1290
+ "RV": (Code._1FC9, 00, Code._12A0, 0x000000, 01, Code._10E0, 0x000000, 00, Code._31E0, 0x000000, 00, Code._1FC9, 0x000000") RFT-RV (04-00046)
1291
+ "CO2": (Code._1FC9, 00, Code._1298, 0x000000, 00, Code._2E10, 0x000000, 01, Code._10E0, 0x000000, 00, Code._31E0, 0x000000, 00, Code._1FC9, 0x000000") RFT-CO2 (04-00045)
1292
+ ========== ================= ===================== ========================= ========================== ========================= ========================== ================= ==========
1293
+
1294
+ Leave commands:
1295
+ ---------------
1296
+
1297
+ .. table:: leave per accessory type
1298
+ :widths: auto
1299
+
1300
+ ========== ================= ====================== ========================= ==========
1301
+ type set 1 set 2 description art #
1302
+ ========== ================= ====================== ========================= ==========
1303
+ "Others": (Code._1FC9, 00, Code._1FC9, 0x000000") standard leave command
1304
+ "AUTORFT": (Code._1FC9, 63, Code._1FC9, 0x000000") leave command of AUTO RFT (536-0150)
1305
+ ========== ================= ====================== ========================= ==========
1306
+
1307
+ .. table:: verbs
1308
+ :widths: 2, 4
1309
+
1310
+ ====== ========
1311
+ verb byte
1312
+ ====== ========
1313
+ ``RQ`` ``0x00``
1314
+ ``I_`` ``0x01``
1315
+ ``W_`` ``0x02``
1316
+ ``RP`` ``0x03``
1317
+ ====== ========
1258
1318
 
1259
1319
  """
ramses_rf/dispatcher.py CHANGED
@@ -186,6 +186,12 @@ def process_msg(gwy: Gateway, msg: Message) -> None:
186
186
  # which requires a valid payload only for 000C.
187
187
 
188
188
  def logger_xxxx(msg: Message) -> None:
189
+ """
190
+ Log msg according to src, code, log.debug setting.
191
+
192
+ :param msg: the Message being processed
193
+ :return: None
194
+ """
189
195
  if _DBG_FORCE_LOG_MESSAGES:
190
196
  _LOGGER.warning(msg)
191
197
  elif msg.src != gwy.hgi or (msg.code != Code._PUZZ and msg.verb != RQ):
@@ -268,10 +274,9 @@ def process_msg(gwy: Gateway, msg: Message) -> None:
268
274
 
269
275
  else:
270
276
  logger_xxxx(msg)
271
- # if gwy.msg_db:
272
- # gwy.msg_db.add(
273
- # msg
274
- # ) # why add it? passes all tests without
277
+ if gwy.msg_db:
278
+ gwy.msg_db.add(msg)
279
+ # why add it? enable for evohome
275
280
 
276
281
 
277
282
  # TODO: this needs cleaning up (e.g. handle intervening packet)