ramses-rf 0.52.1__py3-none-any.whl → 0.52.2__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/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}"
@@ -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
  """
@@ -422,6 +436,7 @@ class MessageIndex:
422
436
  def qry_dtms(self, **kwargs: bool | dt | str) -> list[Any]:
423
437
  """
424
438
  Select from the ImageIndex 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
@@ -1263,6 +1263,7 @@ class BdrSwitch(Actuator, RelayDemand): # BDR (13):
1263
1263
  """The BDR class, such as a BDR91.
1264
1264
 
1265
1265
  BDR91s can be used in six distinct modes, including:
1266
+
1266
1267
  - x2 boiler controller (FC/TPI): either traditional, or newer heat pump-aware
1267
1268
  - x1 electric heat zones (0x/ELE)
1268
1269
  - x1 zone valve zones (0x/VAL)
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)
ramses_rf/entity_base.py CHANGED
@@ -172,7 +172,31 @@ class _Entity:
172
172
 
173
173
 
174
174
  class _MessageDB(_Entity):
175
- """Maintain/utilize an entity's state database."""
175
+ """Maintain/utilize an entity's state database.
176
+
177
+ EntityBase msg_db query methods
178
+
179
+ (ix = database.py.MessageIndex method)
180
+
181
+ .. table:: Database Query Methods
182
+ :widths: auto
183
+
184
+ ==== ====================== ==================== ============ ========== ==========
185
+ e. method name args returns uses used by
186
+ ==== ====================== ==================== ============ ========== ==========
187
+ e1 _get_msg_by_hdr hdr Message i3 discover
188
+ e2 _msg_value code(s), Msg, args dict[k,v] e3,e4
189
+ e3 _msg_value_code code, verb, key dict[k,v] e4,e5,e6 e6
190
+ e4 _msg_value_msg Msg, (code) dict[k,v] e2,e3
191
+ e5 _msg_qry_by_code_key code, key, (verb=) e6,
192
+ e6 _msg_value_qry_by_code key code, key str/float e3,e5
193
+ e7 _msg_qry sql e8
194
+ e8 _msg_count sql e7
195
+ e9 supported_cmds list(Codes) i7
196
+ e10 _msgs() i5
197
+ ==== ====================== ==================== ============ ========== ==========
198
+
199
+ """
176
200
 
177
201
  _gwy: Gateway
178
202
  ctl: Controller
@@ -220,7 +244,13 @@ class _MessageDB(_Entity):
220
244
  return # don't store the rest
221
245
 
222
246
  if self._gwy.msg_db: # central SQLite MessageIndex
223
- self._gwy.msg_db.add(msg)
247
+ _LOGGER.debug(
248
+ "For %s (z_id %s) add msg %s, src %s, dst %s to msg_db.",
249
+ self.id,
250
+ self._z_id,
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
@@ -271,7 +302,12 @@ 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
+ _LOGGER.debug(
306
+ "_msg_list could not fetch self._msgs[%s] for %s (z_id %s)",
307
+ c,
308
+ self.id,
309
+ self._z_id,
310
+ )
275
311
  return msg_list_qry
276
312
  # else create from legacy nested dict
277
313
  return [m for c in self._msgz.values() for v in c.values() for m in v.values()]
@@ -316,32 +352,7 @@ class _MessageDB(_Entity):
316
352
  with contextlib.suppress(KeyError):
317
353
  del obj._msgz_[msg.code][msg.verb][msg._pkt._ctx]
318
354
 
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
- # +----+----------------------+--------------------+------------+----------+----------+
355
+ ### entity_base query methods
345
356
 
346
357
  def _get_msg_by_hdr(self, hdr: HeaderT) -> Message | None:
347
358
  """Return a msg, if any, that matches a given header."""
@@ -549,7 +560,7 @@ class _MessageDB(_Entity):
549
560
  Retrieve from the MessageIndex the most current Code for a code(s) &
550
561
  keyword combination involving this device.
551
562
 
552
- :param code: (optional) a message Code to use, e.g. 31DA or a tuple of Codes
563
+ :param code: (optional) a message Code to use, e.g. Code._31DA or a tuple of Codes
553
564
  :param key: (optional) message keyword to fetch, e.g. SZ_HUMIDITY
554
565
  :param kwargs: optional verb='vb' single verb
555
566
  :return: Code of most recent query result message or None when query returned empty
@@ -582,7 +593,9 @@ class _MessageDB(_Entity):
582
593
  for rec in self._gwy.msg_db.qry_field(
583
594
  sql, (vb, self.id[:_SQL_SLICE], self.id[:_SQL_SLICE], code_qry, key)
584
595
  ):
585
- _LOGGER.debug("Fetched from index: %s", rec)
596
+ _LOGGER.debug(
597
+ "_msg_qry_by_code_key fetched rec: %s, code: %s", rec, code_qry
598
+ )
586
599
  assert isinstance(rec[0], dt) # mypy hint
587
600
  if rec[0] > latest: # dtm, only use most recent
588
601
  res = Code(rec[1])
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/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.2"
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.2
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,30 +4,30 @@ 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
7
+ ramses_rf/__init__.py,sha256=vp2TyFGqc1fGQHsevhmaw0QEmSSCnZx7fqizKiEwHtw,1245
8
8
  ramses_rf/binding_fsm.py,sha256=uZAOl3i19KCXqqlaLJWkEqMMP7NJBhVPW3xTikQD1fY,25996
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=pfCOt6wvABAaW275mNLoRnObFSQ9nT5d7PtDLMTvgr4,20340
11
+ ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
12
+ ramses_rf/entity_base.py,sha256=OPdQEeJ5zk49FKCFmRui4urFQVR2tLBS1iWCTWeZkNo,55712
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=3xbuWkwVUOezgu-nlN98fGrnAgqtfivOcMVKjCTSBds,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=hvTX32Evp23vqjXu-YHqTuneem4_QBRSxCkYR7dg4VI,54705
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
27
  ramses_rf/system/zones.py,sha256=9AH7ooN5QfiqvWuor2P1Dn8aILjQb2RWL9rWqDH1IjA,36075
28
- ramses_tx/__init__.py,sha256=qNMTe8hBkIuecvtCiekUB0pKdD8atb0SjWxVNVe3yhE,3538
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
@@ -38,18 +38,18 @@ ramses_tx/logger.py,sha256=qYbUoNPnPaFWKVsYvLG6uTVuPTdZ8HsMzBbGx0DpBqc,10177
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=U8SkwbW41zac0MQe0NgP6qTUqymmbeCG-UHPEw_GAv0,13267
47
+ ramses_tx/transport.py,sha256=kyvnVf2RpuA_8YRO6C4S60YHctDag0ywfoMzb0wWDGY,58552
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=NEkkAJQW7Hx8GmvkR9BZpLSOi30OMi9Awth13sWXuTQ,123
51
+ ramses_rf-0.52.2.dist-info/METADATA,sha256=Fm3vwa0FvFkMEHz44B8FDq0PjboTn_g9sGHyK7HiydA,4000
52
+ ramses_rf-0.52.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
+ ramses_rf-0.52.2.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
54
+ ramses_rf-0.52.2.dist-info/licenses/LICENSE,sha256=-Kc35W7l1UkdiQ4314_yVWv7vDDrg7IrJfMLUiq6Nfs,1074
55
+ ramses_rf-0.52.2.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/parsers.py CHANGED
@@ -3,9 +3,11 @@
3
3
 
4
4
  NOTES: aspirations on a consistent Schema, going forward:
5
5
 
6
- :mode/state: | :bool: | :mutex (infinitive. vs -ing): | :flags:
7
- mode (config.) | enabled | disabled, heat, cool, heat_cool... | ch_enabled, dhw_enabled
8
- state (action) | active | idle, heating, cooling... | is_heating, is_cooling
6
+ ============== ======== =================================== ========================
7
+ :mode/state: :bool: :mutex (infinitive. vs -ing): :flags:
8
+ mode (config.) enabled disabled, heat, cool, heat_cool... ch_enabled, dhw_enabled
9
+ state (action) active idle, heating, cooling... is_heating, is_cooling
10
+ ============== ======== =================================== ========================
9
11
 
10
12
  - prefer: enabled: True over xx_enabled: True (if only ever 1 flag)
11
13
  - prefer: active: True over is_heating: True (if only ever 1 flag)
@@ -362,8 +364,9 @@ def parser_0009(payload: str, msg: Message) -> dict | list[dict]: # TODO: only
362
364
 
363
365
  The failsafe mode defines the relay behaviour if the RF communication is lost (e.g.
364
366
  when a room thermostat stops communicating due to discharged batteries):
365
- False (disabled) - if RF comms are lost, relay will be held in OFF position
366
- True (enabled) - if RF comms are lost, relay will cycle at 20% ON, 80% OFF
367
+
368
+ - False (disabled) - if RF comms are lost, relay will be held in OFF position
369
+ - True (enabled) - if RF comms are lost, relay will cycle at 20% ON, 80% OFF
367
370
 
368
371
  This setting may need to be enabled to ensure frost protect mode.
369
372
  """
@@ -2289,45 +2292,46 @@ def parser_31da(payload: str, msg: Message) -> PayDictT._31DA:
2289
2292
  def parser_31e0(payload: str, msg: Message) -> dict | list[dict]: # TODO: only dict
2290
2293
  """Notes are.
2291
2294
 
2292
- van means of”.
2295
+ "van" means "of".
2293
2296
  - 0 = min. van min. potm would be:
2294
2297
  - 0 = minimum of minimum potentiometer
2295
2298
 
2296
2299
  See: https://www.industrialcontrolsonline.com/honeywell-t991a
2297
2300
  - modulates air temperatures in ducts
2298
-
2299
- case 0x31E0: ' 12768:
2300
- {
2301
- string str4;
2302
- unchecked
2303
- {
2304
- result.Fan = Conversions.ToString((double)(int)data[checked(start + 1)] / 2.0);
2305
- str4 = "";
2306
- }
2307
- str4 = (data[start + 2] & 0xF) switch
2308
- {
2309
- 0 => str4 + "0 = min. potm. ",
2310
- 1 => str4 + "0 = min. van min. potm ",
2311
- 2 => str4 + "0 = min. fan ",
2312
- _ => "",
2313
- };
2314
- switch (data[start + 2] & 0xF0)
2315
- {
2316
- case 16:
2317
- str4 += "100 = max. potm";
2318
- break;
2319
- case 32:
2320
- str4 += "100 = max. van max. potm ";
2321
- break;
2322
- case 48:
2323
- str4 += "100 = max. fan ";
2324
- break;
2325
- }
2326
- result.Data = str4;
2327
- break;
2328
- }
2329
2301
  """
2330
2302
 
2303
+ # coding note:
2304
+ # case 0x31E0: ' 12768:
2305
+ # {
2306
+ # string str4;
2307
+ # unchecked
2308
+ # {
2309
+ # result.Fan = Conversions.ToString((double)(int)data[checked(start + 1)] / 2.0);
2310
+ # str4 = "";
2311
+ # }
2312
+ # str4 = (data[start + 2] & 0xF) switch
2313
+ # {
2314
+ # 0 => str4 + "0 = min. potm. ",
2315
+ # 1 => str4 + "0 = min. van min. potm ",
2316
+ # 2 => str4 + "0 = min. fan ",
2317
+ # _ => "",
2318
+ # };
2319
+ # switch (data[start + 2] & 0xF0)
2320
+ # {
2321
+ # case 16:
2322
+ # str4 += "100 = max. potm";
2323
+ # break;
2324
+ # case 32:
2325
+ # str4 += "100 = max. van max. potm ";
2326
+ # break;
2327
+ # case 48:
2328
+ # str4 += "100 = max. fan ";
2329
+ # break;
2330
+ # }
2331
+ # result.Data = str4;
2332
+ # break;
2333
+ # }
2334
+
2331
2335
  # .I --- 37:005302 32:132403 --:------ 31E0 008 00-0000-00 01-0064-00 # RF15 CO2 to Orcon HRC400 series SmartComfort Valve
2332
2336
 
2333
2337
  # .I --- 29:146052 32:023459 --:------ 31E0 003 00-0000
ramses_tx/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 protocol (lower) layer.
4
+ :term:`Schema` processor for protocol (lower) layer.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
@@ -73,13 +73,17 @@ class PktLogConfigT(TypedDict):
73
73
  def sch_packet_log_dict_factory(
74
74
  default_backups: int = 0,
75
75
  ) -> dict[vol.Required, vol.Any]:
76
- """Return a packet log dict with a configurable default rotation policy.
76
+ """
77
+ :return: a packet log dict with a configurable default rotation policy.
77
78
 
78
- usage:
79
+ Usage:
80
+
81
+ .. code-block::
82
+
83
+ SCH_PACKET_LOG_7 = vol.Schema(
84
+ packet_log_dict_factory(default_backups=7), extra=vol.PREVENT_EXTRA
85
+ )
79
86
 
80
- SCH_PACKET_LOG_7 = vol.Schema(
81
- packet_log_dict_factory(default_backups=7), extra=vol.PREVENT_EXTRA
82
- )
83
87
  """
84
88
 
85
89
  SCH_PACKET_LOG_CONFIG = vol.Schema(
@@ -162,11 +166,13 @@ class PortConfigT(TypedDict):
162
166
  def sch_serial_port_dict_factory() -> dict[vol.Required, vol.Any]:
163
167
  """Return a serial port dict.
164
168
 
165
- usage:
169
+ Usage:
166
170
 
167
- SCH_SERIAL_PORT = vol.Schema(
168
- sch_serial_port_dict_factory(), extra=vol.PREVENT_EXTRA
169
- )
171
+ .. code-block::
172
+
173
+ SCH_SERIAL_PORT = vol.Schema(
174
+ sch_serial_port_dict_factory(), extra=vol.PREVENT_EXTRA
175
+ )
170
176
  """
171
177
 
172
178
  SCH_SERIAL_PORT_NAME = str
@@ -252,11 +258,13 @@ def sch_global_traits_dict_factory(
252
258
  ) -> tuple[dict[vol.Optional, vol.Any], vol.Any]:
253
259
  """Return a global traits dict with a configurable extra traits.
254
260
 
255
- usage:
261
+ Usage:
256
262
 
257
- SCH_GLOBAL_TRAITS = vol.Schema(
258
- sch_global_traits_dict(heat=traits), extra=vol.PREVENT_EXTRA
259
- )
263
+ .. code-block::
264
+
265
+ SCH_GLOBAL_TRAITS = vol.Schema(
266
+ sch_global_traits_dict(heat=traits), extra=vol.PREVENT_EXTRA
267
+ )
260
268
  """
261
269
 
262
270
  heat_traits = heat_traits or {}
@@ -348,7 +356,8 @@ def select_device_filter_mode(
348
356
  known_list: DeviceListT,
349
357
  block_list: DeviceListT,
350
358
  ) -> bool:
351
- """Determine which device filter to use, if any.
359
+ """
360
+ Determine which device filter to use, if any.
352
361
 
353
362
  Either:
354
363
  - block if device_id in block_list (could be empty), otherwise
ramses_tx/transport.py CHANGED
@@ -3,30 +3,38 @@
3
3
 
4
4
  Operates at the pkt layer of: app - msg - pkt - h/w
5
5
 
6
- For ser2net, use the following YAML with: ser2net -c misc/ser2net.yaml
7
- connection: &con00
8
- accepter: telnet(rfc2217),tcp,5001
9
- timeout: 0
10
- connector: serialdev,/dev/ttyUSB0,115200n81,local
11
- options:
12
- max-connections: 3
13
-
14
- For socat, see:
15
- socat -dd pty,raw,echo=0 pty,raw,echo=0
16
- python client.py monitor /dev/pts/0
17
- cat packet.log | cut -d ' ' -f 2- | unix2dos > /dev/pts/1
6
+ For ser2net, use the following YAML with: ``ser2net -c misc/ser2net.yaml``
7
+
8
+ .. code-block::
9
+
10
+ connection: &con00
11
+ accepter: telnet(rfc2217),tcp,5001
12
+ timeout: 0
13
+ connector: serialdev,/dev/ttyUSB0,115200n81,local
14
+ options:
15
+ max-connections: 3
16
+
17
+ For ``socat``, see:
18
+
19
+ .. code-block::
20
+
21
+ socat -dd pty,raw,echo=0 pty,raw,echo=0
22
+ python client.py monitor /dev/pts/0
23
+ cat packet.log | cut -d ' ' -f 2- | unix2dos > /dev/pts/1
18
24
 
19
25
  For re-flashing evofw3 via Arduino IDE on *my* atmega328p (YMMV):
20
- - Board: atmega328p (SW UART)
21
- - Bootloader: Old Bootloader
22
- - Processor: atmega328p (5V, 16 MHz)
23
- - Host: 57600 (or 115200, YMMV)
24
- - Pinout: Nano
26
+
27
+ - Board: atmega328p (SW UART)
28
+ - Bootloader: Old Bootloader
29
+ - Processor: atmega328p (5V, 16 MHz)
30
+ - Host: 57600 (or 115200, YMMV)
31
+ - Pinout: Nano
25
32
 
26
33
  For re-flashing evofw3 via Arduino IDE on *my* atmega32u4 (YMMV):
27
- - Board: atmega32u4 (HW UART)
28
- - Processor: atmega32u4 (5V, 16 MHz)
29
- - Pinout: Pro Micro
34
+
35
+ - Board: atmega32u4 (HW UART)
36
+ - Processor: atmega32u4 (5V, 16 MHz)
37
+ - Pinout: Pro Micro
30
38
  """
31
39
 
32
40
  from __future__ import annotations
@@ -250,11 +258,12 @@ async def is_hgi80(serial_port: SerPortNameT) -> bool | None:
250
258
 
251
259
 
252
260
  def _normalise(pkt_line: str) -> str:
253
- """Perform any (transparent) frame-level hacks, as required at (near-)RF layer.
261
+ """
262
+ Perform any (transparent) frame-level hacks, as required at (near-)RF layer.
254
263
 
255
264
  Goals:
256
265
  - ensure an evofw3 provides the same output as a HGI80 (none, presently)
257
- - handle 'strange' packets (e.g. I|08:|0008)
266
+ - handle 'strange' packets (e.g. ``I|08:|0008``)
258
267
  """
259
268
 
260
269
  # TODO: deprecate as only for ramses_esp <0.4.0
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.52.1"
3
+ __version__ = "0.52.2"
4
4
  VERSION = __version__