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 +1 -0
- ramses_rf/database.py +55 -37
- ramses_rf/device/heat.py +1 -0
- ramses_rf/device/hvac.py +116 -56
- ramses_rf/dispatcher.py +9 -4
- ramses_rf/entity_base.py +44 -31
- ramses_rf/gateway.py +2 -2
- ramses_rf/schemas.py +1 -1
- ramses_rf/version.py +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.2.dist-info}/METADATA +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.2.dist-info}/RECORD +20 -20
- ramses_tx/__init__.py +3 -1
- ramses_tx/command.py +6 -5
- ramses_tx/parsers.py +41 -37
- ramses_tx/schemas.py +24 -15
- ramses_tx/transport.py +31 -22
- ramses_tx/version.py +1 -1
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.2.dist-info}/WHEEL +0 -0
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.2.dist-info}/entry_points.txt +0 -0
- {ramses_rf-0.52.1.dist-info → ramses_rf-0.52.2.dist-info}/licenses/LICENSE +0 -0
ramses_rf/__init__.py
CHANGED
ramses_rf/database.py
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
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
|
|
66
|
-
Index holds the latest
|
|
67
|
-
(
|
|
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.
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
|
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
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
"
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
"
|
|
1224
|
-
"
|
|
1225
|
-
"
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
"
|
|
1246
|
-
"
|
|
1247
|
-
"
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
"
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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
|
|
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/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.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=
|
|
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=
|
|
11
|
-
ramses_rf/dispatcher.py,sha256=
|
|
12
|
-
ramses_rf/entity_base.py,sha256=
|
|
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=
|
|
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=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=
|
|
22
|
-
ramses_rf/device/hvac.py,sha256=
|
|
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=
|
|
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
|
|
@@ -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=
|
|
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=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=
|
|
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=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
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/parsers.py
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
NOTES: aspirations on a consistent Schema, going forward:
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
mode (
|
|
8
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
|
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
|
-
"""
|
|
76
|
+
"""
|
|
77
|
+
:return: a packet log dict with a configurable default rotation policy.
|
|
77
78
|
|
|
78
|
-
|
|
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
|
-
|
|
169
|
+
Usage:
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
261
|
+
Usage:
|
|
256
262
|
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
"""
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
"""
|
|
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
|
File without changes
|
|
File without changes
|
|
File without changes
|