ramses-rf 0.22.2__py3-none-any.whl → 0.51.1__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.
Files changed (72) hide show
  1. ramses_cli/__init__.py +18 -0
  2. ramses_cli/client.py +597 -0
  3. ramses_cli/debug.py +20 -0
  4. ramses_cli/discovery.py +405 -0
  5. ramses_cli/utils/cat_slow.py +17 -0
  6. ramses_cli/utils/convert.py +60 -0
  7. ramses_rf/__init__.py +31 -10
  8. ramses_rf/binding_fsm.py +787 -0
  9. ramses_rf/const.py +124 -105
  10. ramses_rf/database.py +297 -0
  11. ramses_rf/device/__init__.py +69 -39
  12. ramses_rf/device/base.py +187 -376
  13. ramses_rf/device/heat.py +540 -552
  14. ramses_rf/device/hvac.py +286 -171
  15. ramses_rf/dispatcher.py +153 -177
  16. ramses_rf/entity_base.py +478 -361
  17. ramses_rf/exceptions.py +82 -0
  18. ramses_rf/gateway.py +378 -514
  19. ramses_rf/helpers.py +57 -19
  20. ramses_rf/py.typed +0 -0
  21. ramses_rf/schemas.py +148 -194
  22. ramses_rf/system/__init__.py +16 -23
  23. ramses_rf/system/faultlog.py +363 -0
  24. ramses_rf/system/heat.py +295 -302
  25. ramses_rf/system/schedule.py +312 -198
  26. ramses_rf/system/zones.py +318 -238
  27. ramses_rf/version.py +2 -8
  28. ramses_rf-0.51.1.dist-info/METADATA +72 -0
  29. ramses_rf-0.51.1.dist-info/RECORD +55 -0
  30. {ramses_rf-0.22.2.dist-info → ramses_rf-0.51.1.dist-info}/WHEEL +1 -2
  31. ramses_rf-0.51.1.dist-info/entry_points.txt +2 -0
  32. {ramses_rf-0.22.2.dist-info → ramses_rf-0.51.1.dist-info/licenses}/LICENSE +1 -1
  33. ramses_tx/__init__.py +160 -0
  34. {ramses_rf/protocol → ramses_tx}/address.py +65 -59
  35. ramses_tx/command.py +1454 -0
  36. ramses_tx/const.py +903 -0
  37. ramses_tx/exceptions.py +92 -0
  38. {ramses_rf/protocol → ramses_tx}/fingerprints.py +56 -15
  39. {ramses_rf/protocol → ramses_tx}/frame.py +132 -131
  40. ramses_tx/gateway.py +338 -0
  41. ramses_tx/helpers.py +883 -0
  42. {ramses_rf/protocol → ramses_tx}/logger.py +67 -53
  43. {ramses_rf/protocol → ramses_tx}/message.py +155 -191
  44. ramses_tx/opentherm.py +1260 -0
  45. ramses_tx/packet.py +210 -0
  46. ramses_tx/parsers.py +2957 -0
  47. ramses_tx/protocol.py +801 -0
  48. ramses_tx/protocol_fsm.py +672 -0
  49. ramses_tx/py.typed +0 -0
  50. {ramses_rf/protocol → ramses_tx}/ramses.py +262 -185
  51. {ramses_rf/protocol → ramses_tx}/schemas.py +150 -133
  52. ramses_tx/transport.py +1471 -0
  53. ramses_tx/typed_dicts.py +492 -0
  54. ramses_tx/typing.py +181 -0
  55. ramses_tx/version.py +4 -0
  56. ramses_rf/discovery.py +0 -398
  57. ramses_rf/protocol/__init__.py +0 -59
  58. ramses_rf/protocol/backports.py +0 -42
  59. ramses_rf/protocol/command.py +0 -1561
  60. ramses_rf/protocol/const.py +0 -697
  61. ramses_rf/protocol/exceptions.py +0 -111
  62. ramses_rf/protocol/helpers.py +0 -390
  63. ramses_rf/protocol/opentherm.py +0 -1170
  64. ramses_rf/protocol/packet.py +0 -235
  65. ramses_rf/protocol/parsers.py +0 -2673
  66. ramses_rf/protocol/protocol.py +0 -613
  67. ramses_rf/protocol/transport.py +0 -1011
  68. ramses_rf/protocol/version.py +0 -10
  69. ramses_rf/system/hvac.py +0 -82
  70. ramses_rf-0.22.2.dist-info/METADATA +0 -64
  71. ramses_rf-0.22.2.dist-info/RECORD +0 -42
  72. ramses_rf-0.22.2.dist-info/top_level.txt +0 -1
@@ -1,1170 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- #
4
- """RAMSES RF - Opentherm processor."""
5
- from __future__ import annotations
6
-
7
- import logging
8
- import struct
9
- from datetime import timedelta as td
10
- from types import SimpleNamespace
11
- from typing import Callable, Dict
12
-
13
- from .const import __dev_mode__
14
-
15
- DEV_MODE = __dev_mode__ and False
16
-
17
- _LOGGER = logging.getLogger(__name__)
18
- if DEV_MODE:
19
- _LOGGER.setLevel(logging.DEBUG)
20
-
21
- # R8810A/R8820A-specific msg_ids, as used by Packet (pkt_timeout) & OtbGateway classes
22
- SCHEMA_MSG_IDS: tuple = (
23
- "03", # ..3: "Slave configuration",
24
- "06", # ..6: "Remote boiler parameter flags", # see: 0x38, 0x39
25
- "7D", # 125: "Opentherm version Slave",
26
- "7F", # 127: "Slave product version number and type",
27
- # These are STATUS seen RQ'd by 01:/30:, but here to retrieve less frequently
28
- "71", # 113: "Number of un-successful burner starts", # is R/W
29
- "72", # 114: "Number of times flame signal was too low", # is R/W
30
- "74", # 116: "Number of starts burner", # is R/W
31
- "75", # 117: "Number of starts central heating pump", # is R/W
32
- "76", # 118: "Number of starts DHW pump/valve", # is R/W
33
- "77", # 119: "Number of starts burner during DHW mode", # is R/W
34
- "78", # 120: "Number of hours burner is in operation (i.e. flame on)", # is R/W
35
- "79", # 121: "Number of hours central heating pump has been running", # is R/W
36
- "7A", # 122: "Number of hours DHW pump has been running/valve has been opened", # is R/W
37
- "7B", # 123: "Number of hours DHW burner is in operation during DHW mode", # is R/W
38
- )
39
- # SCHEMA_MSG_IDS = {k: False for k in SCHEMA_MSG_IDS}
40
- _PARAMS_MSG_IDS: tuple = (
41
- "0E", # .14: "Maximum relative modulation level setting (%)", # NOTE: is W only!
42
- "0F", # .15: "Max. boiler capacity (kW) and modulation level setting (%)",
43
- "30", # .48: "DHW Setpoint upper & lower bounds for adjustment (°C)",
44
- "31", # .49: "Max CH water Setpoint upper & lower bounds for adjustment (°C)",
45
- "38", # .56: "DHW Setpoint (°C) (Remote parameter 1)", # see: 0x06, is R/W
46
- "39", # .57: "Max CH water Setpoint (°C) (Remote parameter 2)", # see: 0x06, is R/W
47
- )
48
- PARAMS_MSG_IDS: dict = {k: td(hours=6) for k in _PARAMS_MSG_IDS}
49
- _STATUS_MSG_IDS: tuple = (
50
- "00", # ..0: "Master/Slave status flags",
51
- "01", # ..1: "CH water temperature Setpoint (°C)", # NOTE: is W only!
52
- "11", # .17: "Relative Modulation Level (%)",
53
- "12", # .18: "Water pressure in CH circuit (bar)",
54
- "13", # .19: "Water flow rate in DHW circuit. (L/min)",
55
- "19", # .25: "Boiler flow water temperature (°C)",
56
- "1A", # .26: "DHW temperature (°C)",
57
- "1B", # .27: "Outside temperature (°C)", # TODO: any value here? # is R/W
58
- "1C", # .28: "Return water temperature (°C)",
59
- # These are error/state codes...
60
- "05", # ..5: "Fault flags & OEM codes",
61
- "73", # 115: "OEM diagnostic code",
62
- )
63
- STATUS_MSG_IDS: dict = {k: td(minutes=5) for k in _STATUS_MSG_IDS}
64
- _WRITE_MSG_IDS: tuple = ( # Write-Data, NB: some are also Read-Data
65
- "01", # ..1: "CH water temperature Setpoint (°C)",
66
- "02", # ..2: "Master configuration",
67
- "09", # ..9: "Remote override room Setpoint (°C)",
68
- "0E", # .14: "Maximum relative modulation level setting (%)", # c.f. 0x11
69
- "10", # .16: "Room Setpoint (°C)", # tell slave the room setpoint?
70
- "18", # .24: "Room temperature (°C)", # tell slave the room temp?
71
- "1B", # .27: "Outside temperature (°C)", # is R/W
72
- "38", # .56: -see above- # is R/W
73
- "39", # .57: -see above- # is R/W
74
- "7C", # 124: "Opentherm version Master",
75
- "7E", # 126: "Master product version number and type",
76
- )
77
- WRITE_MSG_IDS: dict = {k: td(seconds=3) for k in _WRITE_MSG_IDS}
78
-
79
- # Data structure shamelessy copied, with thanks to @nlrb, from:
80
- # github.com/nlrb/com.tclcode.otgw (node_modules/otg-api/lib/ot_msg.js),
81
-
82
- # Other code shamelessy copied, with thanks to @mvn23, from:
83
- # github.com/mvn23/pyotgw (pyotgw/protocol.py),
84
-
85
- # Also see:
86
- # github.com/rvdbreemen/OTGW-firmware
87
-
88
- READ_WRITE: str = "RW"
89
- READ_ONLY: str = "R-"
90
- WRITE_ONLY: str = "-W"
91
-
92
- EN: str = "en"
93
- FLAGS: str = "flags"
94
- DIR: str = "dir"
95
- NL: str = "nl"
96
- SENSOR: str = "sensor"
97
- VAL: str = "val"
98
- VAR: str = "var"
99
-
100
- FLAG8: str = "flag8"
101
- FLAG: str = "flag"
102
- U8: str = "u8"
103
- S8: str = "s8"
104
- F8_8: str = "f8.8"
105
- U16: str = "u16"
106
- S16: str = "s16"
107
- SPECIAL: str = U8 # used for ID 0x14 (20)
108
-
109
- HB: str = "hb"
110
- LB: str = "lb"
111
-
112
- MESSAGES: str = "messages"
113
- MSG_DESC: str = "description"
114
- MSG_ID: str = "msg_id"
115
- MSG_NAME: str = "msg_name"
116
- MSG_TYPE: str = "msg_type"
117
- VALUE: str = "value"
118
- VALUE_HB: str = f"{VALUE}_{HB}"
119
- VALUE_LB: str = f"{VALUE}_{LB}"
120
-
121
- Sensor = SimpleNamespace(
122
- COUNTER="counter",
123
- RATIO="ratio",
124
- HUMIDITY="relative humidity (%)",
125
- PERCENTAGE="percentage (%)",
126
- PRESSURE="pressure (bar)",
127
- TEMPERATURE="temperature (°C)",
128
- CURRENT="current (µA)",
129
- FLOW_RATE="flow rate (L/min)",
130
- CO2_LEVEL="CO2 (ppm)",
131
- ) # all are F8_8, except COUNTER, CO2_LEVEL
132
-
133
-
134
- OtMsgType = SimpleNamespace(
135
- READ_DATA="Read-Data",
136
- WRITE_DATA="Write-Data",
137
- INVALID_DATA="Invalid-Data",
138
- RESERVED="-reserved-",
139
- READ_ACK="Read-Ack",
140
- WRITE_ACK="Write-Ack",
141
- DATA_INVALID="Data-Invalid",
142
- UNKNOWN_DATAID="Unknown-DataId",
143
- ) # all are F8_8, except COUNTER, CO2_LEVEL
144
-
145
- OPENTHERM_MSG_TYPE: dict = {
146
- 0b000: OtMsgType.READ_DATA,
147
- 0b001: OtMsgType.WRITE_DATA,
148
- 0b010: OtMsgType.INVALID_DATA,
149
- 0b011: OtMsgType.RESERVED, # as per Unknown-DataId?
150
- 0b100: OtMsgType.READ_ACK,
151
- 0b101: OtMsgType.WRITE_ACK,
152
- 0b110: OtMsgType.DATA_INVALID, # e.g. sensor fault
153
- 0b111: OtMsgType.UNKNOWN_DATAID,
154
- }
155
-
156
- # These must have either a FLAGS (preferred) or a VAR for their message name
157
- OPENTHERM_SCHEMA: dict = {
158
- # OpenTherm status flags [ID 0: Master status (HB) & Slave status (LB)]
159
- "status_flags": {
160
- 0x0100: {
161
- EN: "Central heating enable",
162
- NL: "Centrale verwarming aan",
163
- VAR: "StatusCHEnabled",
164
- }, # CH enabled
165
- 0x0200: {
166
- EN: "DHW enable",
167
- NL: "Tapwater aan",
168
- VAR: "StatusDHWEnabled",
169
- }, # DHW enabled
170
- 0x0400: {
171
- EN: "Cooling enable",
172
- NL: "Koeling aan",
173
- VAR: "StatusCoolEnabled",
174
- }, # cooling enabled
175
- 0x0800: {
176
- EN: "Outside temp. comp. active",
177
- NL: "Compenseren buitentemp.",
178
- VAR: "StatusOTCActive",
179
- }, # OTC active
180
- 0x1000: {
181
- EN: "Central heating 2 enable",
182
- NL: "Centrale verwarming 2 aan",
183
- VAR: "StatusCH2Enabled",
184
- }, # CH2 enabled
185
- 0x2000: {
186
- EN: "Summer/winter mode",
187
- NL: "Zomer/winter mode",
188
- VAR: "StatusSummerWinter",
189
- }, # summer mode active
190
- 0x4000: {
191
- EN: "DHW blocking",
192
- NL: "Tapwater blokkade",
193
- VAR: "StatusDHWBlocked",
194
- }, # DHW is blocking
195
- 0x0001: {
196
- EN: "Fault indication",
197
- NL: "Fout indicatie",
198
- VAR: "StatusFault",
199
- }, # fault state
200
- 0x0002: {
201
- EN: "Central heating mode",
202
- NL: "Centrale verwarming mode",
203
- VAR: "StatusCHMode",
204
- }, # CH active
205
- 0x0004: {
206
- EN: "DHW mode",
207
- NL: "Tapwater mode",
208
- VAR: "StatusDHWMode",
209
- }, # DHW active
210
- 0x0008: {
211
- EN: "Flame status",
212
- NL: "Vlam status",
213
- VAR: "StatusFlame",
214
- }, # flame on
215
- 0x0010: {
216
- EN: "Cooling status",
217
- NL: "Status koelen",
218
- VAR: "StatusCooling",
219
- }, # cooling active
220
- 0x0020: {
221
- EN: "Central heating 2 mode",
222
- NL: "Centrale verwarming 2 mode",
223
- VAR: "StatusCH2Mode",
224
- }, # CH2 active
225
- 0x0040: {
226
- EN: "Diagnostic indication",
227
- NL: "Diagnose indicatie",
228
- VAR: "StatusDiagnostic",
229
- }, # diagnostics mode
230
- },
231
- # OpenTherm Master configuration flags [ID 2: master config flags (HB)]
232
- "master_config_flags": {
233
- 0x0100: {
234
- EN: "Smart Power",
235
- VAR: "ConfigSmartPower",
236
- },
237
- },
238
- # OpenTherm Slave configuration flags [ID 3: slave config flags (HB)]
239
- "slave_config_flags": {
240
- 0x0100: {
241
- EN: "DHW present",
242
- VAR: "ConfigDHWpresent",
243
- },
244
- 0x0200: {
245
- EN: "Control type (modulating on/off)",
246
- VAR: "ConfigControlType",
247
- },
248
- 0x0400: {
249
- EN: "Cooling supported",
250
- VAR: "ConfigCooling",
251
- },
252
- 0x0800: {
253
- EN: "DHW storage tank",
254
- VAR: "ConfigDHW",
255
- },
256
- 0x1000: {
257
- EN: "Master low-off & pump control allowed",
258
- VAR: "ConfigMasterPump",
259
- },
260
- 0x2000: {
261
- EN: "Central heating 2 present",
262
- VAR: "ConfigCH2",
263
- },
264
- },
265
- # OpenTherm fault flags [ID 5: Application-specific fault flags (HB)]
266
- "fault_flags": {
267
- 0x0100: {
268
- EN: "Service request",
269
- NL: "Onderhoudsvraag",
270
- VAR: "FaultServiceRequest",
271
- },
272
- 0x0200: {
273
- EN: "Lockout-reset",
274
- NL: "Geen reset op afstand",
275
- VAR: "FaultLockoutReset",
276
- },
277
- 0x0400: {
278
- EN: "Low water pressure",
279
- NL: "Waterdruk te laag",
280
- VAR: "FaultLowWaterPressure",
281
- },
282
- 0x0800: {
283
- EN: "Gas/flame fault",
284
- NL: "Gas/vlam fout",
285
- VAR: "FaultGasFlame",
286
- },
287
- 0x1000: {
288
- EN: "Air pressure fault",
289
- NL: "Luchtdruk fout",
290
- VAR: "FaultAirPressure",
291
- },
292
- 0x2000: {
293
- EN: "Water over-temperature",
294
- NL: "Water te heet",
295
- VAR: "FaultOverTemperature",
296
- },
297
- },
298
- # OpenTherm remote flags [ID 6: Remote parameter flags (HB)]
299
- "remote_flags": {
300
- 0x0100: {
301
- EN: "DHW setpoint enable",
302
- VAR: "RemoteDHWEnabled",
303
- },
304
- 0x0200: {
305
- EN: "Max. CH setpoint enable",
306
- VAR: "RemoteMaxCHEnabled",
307
- },
308
- 0x0001: {
309
- EN: "DHW setpoint read/write",
310
- VAR: "RemoteDHWReadWrite",
311
- },
312
- 0x0002: {
313
- EN: "Max. CH setpoint read/write",
314
- VAR: "RemoteMaxCHReadWrite",
315
- },
316
- },
317
- # OpenTherm messages
318
- MESSAGES: {
319
- 0x00: { # 0, Status
320
- EN: "Status",
321
- DIR: READ_ONLY,
322
- VAL: {HB: FLAG8, LB: FLAG8},
323
- FLAGS: "status_flags",
324
- },
325
- 0x01: { # 1, Control Setpoint
326
- EN: "Control setpoint",
327
- NL: "Ketel doeltemperatuur",
328
- DIR: WRITE_ONLY,
329
- VAL: F8_8,
330
- VAR: "ControlSetpoint",
331
- SENSOR: Sensor.TEMPERATURE,
332
- },
333
- 0x02: { # 2, Master configuration (Member ID)
334
- EN: "Master configuration",
335
- DIR: WRITE_ONLY,
336
- VAL: {HB: FLAG8, LB: U8},
337
- FLAGS: "master_config_flags",
338
- VAR: {LB: "MasterMemberId"},
339
- },
340
- 0x03: { # 3, Slave configuration (Member ID)
341
- EN: "Slave configuration",
342
- DIR: READ_ONLY,
343
- VAL: {HB: FLAG8, LB: U8},
344
- FLAGS: "slave_config_flags",
345
- VAR: {LB: "SlaveMemberId"},
346
- },
347
- 0x04: { # 4, Remote Command
348
- EN: "Remote command",
349
- DIR: WRITE_ONLY,
350
- VAL: U8,
351
- VAR: "RemoteCommand",
352
- },
353
- 0x05: { # 5, OEM Fault code
354
- EN: "Fault flags & OEM fault code",
355
- DIR: READ_ONLY,
356
- VAL: {HB: FLAG8, LB: U8},
357
- VAR: {LB: "OEMFaultCode"},
358
- FLAGS: "fault_flags",
359
- },
360
- 0x06: { # 6, Remote Flags
361
- EN: "Remote parameter flags",
362
- DIR: READ_ONLY,
363
- VAL: FLAG8,
364
- FLAGS: "remote_flags",
365
- },
366
- 0x07: { # 7, Cooling Control Signal
367
- EN: "Cooling control signal",
368
- DIR: WRITE_ONLY,
369
- VAL: F8_8,
370
- VAR: "CoolingControlSignal",
371
- SENSOR: Sensor.PERCENTAGE,
372
- },
373
- 0x08: { # 8, CH2 Control Setpoint
374
- EN: "Control setpoint for 2nd CH circuit",
375
- DIR: WRITE_ONLY,
376
- VAL: F8_8,
377
- VAR: "CH2ControlSetpoint",
378
- SENSOR: Sensor.TEMPERATURE,
379
- },
380
- 0x09: { # 9, Remote Override Room Setpoint
381
- EN: "Remote override room setpoint",
382
- NL: "Overschreven kamer doeltemperatuur",
383
- DIR: READ_ONLY,
384
- VAL: F8_8,
385
- VAR: "RemoteOverrideRoomSetpoint",
386
- SENSOR: Sensor.TEMPERATURE,
387
- },
388
- 0x0A: { # 10, TSP Number
389
- EN: "Number of transparent slave parameters supported by slave",
390
- DIR: READ_ONLY,
391
- VAL: U8,
392
- VAR: {HB: "TSPNumber"},
393
- },
394
- 0x0B: { # 11, TSP Entry
395
- EN: "Index number/value of referred-to transparent slave parameter",
396
- DIR: READ_WRITE,
397
- VAL: U8,
398
- VAR: {HB: "TSPIndex", LB: "TSPValue"},
399
- },
400
- 0x0C: { # 12, FHB Size
401
- EN: "Size of fault history buffer supported by slave",
402
- DIR: READ_ONLY,
403
- VAL: U8,
404
- VAR: {HB: "FHBSize"},
405
- },
406
- 0x0D: { # 13, FHB Entry
407
- EN: "Index number/value of referred-to fault history buffer entry",
408
- DIR: READ_ONLY,
409
- VAL: U8,
410
- VAR: {HB: "FHBIndex", LB: "FHBValue"},
411
- },
412
- 0x0E: { # 14, Max Relative Modulation Level
413
- EN: "Max. relative modulation level",
414
- NL: "Max. relatief modulatie-niveau",
415
- DIR: WRITE_ONLY,
416
- VAL: F8_8,
417
- VAR: "MaxRelativeModulationLevel",
418
- SENSOR: Sensor.PERCENTAGE,
419
- },
420
- 0x0F: { # 15, Max Boiler Capacity & Min Modulation Level
421
- EN: "Max. boiler capacity (kW) and modulation level setting (%)",
422
- DIR: READ_ONLY,
423
- VAL: U8,
424
- VAR: {HB: "MaxBoilerCapacity", LB: "MinModulationLevel"},
425
- },
426
- 0x10: { # 16, Current Setpoint
427
- EN: "Room setpoint",
428
- NL: "Kamer doeltemperatuur",
429
- DIR: WRITE_ONLY,
430
- VAL: F8_8,
431
- VAR: "CurrentSetpoint",
432
- SENSOR: Sensor.TEMPERATURE,
433
- },
434
- 0x11: { # 17, Relative Modulation Level
435
- EN: "Relative modulation level",
436
- NL: "Relatief modulatie-niveau",
437
- DIR: READ_ONLY,
438
- VAL: F8_8,
439
- VAR: "RelativeModulationLevel",
440
- SENSOR: Sensor.PERCENTAGE,
441
- },
442
- 0x12: { # 18, CH Water Pressure
443
- EN: "Central heating water pressure (bar)",
444
- NL: "Keteldruk",
445
- DIR: READ_ONLY,
446
- VAL: F8_8,
447
- VAR: "CHWaterPressure",
448
- SENSOR: Sensor.PRESSURE,
449
- },
450
- 0x13: { # 19, DHW Flow Rate
451
- EN: "DHW flow rate (litres/minute)",
452
- DIR: READ_ONLY,
453
- VAL: F8_8,
454
- VAR: "DHWFlowRate",
455
- SENSOR: Sensor.FLOW_RATE,
456
- },
457
- 0x14: { # 20, Day/Time
458
- EN: "Day of week & Time of day",
459
- DIR: READ_WRITE,
460
- VAL: {HB: SPECIAL, LB: U8}, # 1..7/0..23, 0..59
461
- VAR: {HB: "DayHour", LB: "Minutes"}, # HB7-5: Day, HB4-0: Hour
462
- },
463
- 0x15: { # 21, Date
464
- EN: "Date",
465
- DIR: READ_WRITE,
466
- VAL: U8, # 1..12, 1..31
467
- VAR: {HB: "Month", LB: "DayOfMonth"},
468
- },
469
- 0x16: { # 22, Year
470
- EN: "Year",
471
- DIR: READ_WRITE,
472
- VAL: U16, # 1999-2099
473
- VAR: "Year",
474
- },
475
- 0x17: { # 23, CH2 Current Setpoint
476
- EN: "Room setpoint for 2nd CH circuit",
477
- DIR: WRITE_ONLY,
478
- VAL: F8_8,
479
- VAR: "CH2CurrentSetpoint",
480
- SENSOR: Sensor.TEMPERATURE,
481
- },
482
- 0x18: { # 24, Current Room Temperature
483
- EN: "Room temperature",
484
- NL: "Kamertemperatuur",
485
- DIR: READ_ONLY,
486
- VAL: F8_8,
487
- VAR: "CurrentTemperature",
488
- SENSOR: Sensor.TEMPERATURE,
489
- },
490
- 0x19: { # 25, Boiler Water Temperature
491
- EN: "Boiler water temperature",
492
- NL: "Ketelwatertemperatuur",
493
- DIR: READ_ONLY,
494
- VAL: F8_8,
495
- VAR: "BoilerWaterTemperature",
496
- SENSOR: Sensor.TEMPERATURE,
497
- },
498
- 0x1A: { # 26, DHW Temperature
499
- EN: "DHW temperature",
500
- NL: "Tapwatertemperatuur",
501
- DIR: READ_ONLY,
502
- VAL: F8_8,
503
- VAR: "DHWTemperature",
504
- SENSOR: Sensor.TEMPERATURE,
505
- },
506
- 0x1B: { # 27, Outside Temperature
507
- EN: "Outside temperature",
508
- NL: "Buitentemperatuur",
509
- DIR: READ_ONLY,
510
- VAL: F8_8,
511
- VAR: "OutsideTemperature",
512
- SENSOR: Sensor.TEMPERATURE,
513
- },
514
- 0x1C: { # 28, Return Water Temperature
515
- EN: "Return water temperature",
516
- NL: "Retourtemperatuur",
517
- DIR: READ_ONLY,
518
- VAL: F8_8,
519
- VAR: "ReturnWaterTemperature",
520
- SENSOR: Sensor.TEMPERATURE,
521
- },
522
- 0x1D: { # 29, Solar Storage Temperature
523
- EN: "Solar storage temperature",
524
- DIR: READ_ONLY,
525
- VAL: F8_8,
526
- VAR: "SolarStorageTemperature",
527
- SENSOR: Sensor.TEMPERATURE,
528
- },
529
- 0x1E: { # 30, Solar Collector Temperature
530
- EN: "Solar collector temperature",
531
- DIR: READ_ONLY,
532
- VAL: F8_8,
533
- VAR: "SolarCollectorTemperature",
534
- SENSOR: Sensor.TEMPERATURE,
535
- },
536
- 0x1F: { # 31, CH2 Flow Temperature
537
- EN: "Flow temperature for 2nd CH circuit",
538
- DIR: READ_ONLY,
539
- VAL: F8_8,
540
- VAR: "CH2FlowTemperature",
541
- SENSOR: Sensor.TEMPERATURE,
542
- },
543
- 0x20: { # 32, DHW2 Temperature
544
- EN: "DHW 2 temperature",
545
- DIR: READ_ONLY,
546
- VAL: F8_8,
547
- VAR: "DHW2Temperature",
548
- SENSOR: Sensor.TEMPERATURE,
549
- },
550
- 0x21: { # 33, Boiler Exhaust Temperature
551
- EN: "Boiler exhaust temperature",
552
- DIR: READ_ONLY,
553
- VAL: S16,
554
- VAR: "BoilerExhaustTemperature",
555
- SENSOR: Sensor.TEMPERATURE,
556
- },
557
- 0x30: { # 48, DHW Boundaries
558
- EN: "DHW setpoint boundaries",
559
- DIR: READ_ONLY,
560
- VAL: S8,
561
- VAR: {HB: "DHWUpperBound", LB: "DHWLowerBound"},
562
- SENSOR: Sensor.TEMPERATURE,
563
- },
564
- 0x31: { # 49, CH Boundaries
565
- EN: "Max. central heating setpoint boundaries",
566
- DIR: READ_ONLY,
567
- VAL: S8,
568
- VAR: {HB: "CHUpperBound", LB: "CHLowerBound"},
569
- SENSOR: Sensor.TEMPERATURE,
570
- },
571
- 0x32: { # 50, OTC Boundaries
572
- EN: "OTC heat curve ratio upper & lower bounds",
573
- DIR: READ_ONLY,
574
- VAL: S8,
575
- VAR: {HB: "OTCUpperBound", LB: "OTCLowerBound"},
576
- },
577
- 0x38: { # 56, DHW Setpoint
578
- EN: "DHW setpoint",
579
- NL: "Tapwater doeltemperatuur",
580
- DIR: READ_WRITE,
581
- VAL: F8_8,
582
- VAR: "DHWSetpoint",
583
- SENSOR: Sensor.TEMPERATURE,
584
- },
585
- 0x39: { # 57, Max CH Water Setpoint
586
- EN: "Max. central heating water setpoint",
587
- NL: "Max. ketel doeltemperatuur",
588
- DIR: READ_WRITE,
589
- VAL: F8_8,
590
- VAR: "MaxCHWaterSetpoint",
591
- SENSOR: Sensor.TEMPERATURE,
592
- },
593
- 0x3A: { # 58, OTC Heat Curve Ratio
594
- EN: "OTC heat curve ratio",
595
- DIR: READ_WRITE,
596
- VAL: F8_8,
597
- VAR: "OTCHeatCurveRatio",
598
- SENSOR: Sensor.RATIO,
599
- },
600
- # OpenTherm 2.3 IDs (70-91) for ventilation/heat-recovery applications
601
- 0x46: { # 70, VH Status
602
- EN: "Status ventilation/heat-recovery",
603
- DIR: READ_ONLY,
604
- VAL: FLAG8,
605
- VAR: "VHStatus",
606
- },
607
- 0x47: { # 71, VH Control Setpoint
608
- EN: "Control setpoint ventilation/heat-recovery",
609
- DIR: WRITE_ONLY,
610
- VAL: U8,
611
- VAR: {HB: "VHControlSetpoint"},
612
- },
613
- 0x48: { # 72, VH Fault code
614
- EN: "Fault flags/code ventilation/heat-recovery",
615
- DIR: READ_ONLY,
616
- VAL: {HB: FLAG, LB: U8},
617
- VAR: {LB: "VHFaultCode"},
618
- },
619
- 0x49: { # 73, VH Diagnostic code
620
- EN: "Diagnostic code ventilation/heat-recovery",
621
- DIR: READ_ONLY,
622
- VAL: U16,
623
- VAR: "VHDiagnosticCode",
624
- },
625
- 0x4A: { # 74, VH Member ID
626
- EN: "Config/memberID ventilation/heat-recovery",
627
- DIR: READ_ONLY,
628
- VAL: {HB: FLAG, LB: U8},
629
- VAR: {LB: "VHMemberId"},
630
- },
631
- 0x4B: { # 75, VH OpenTherm Version
632
- EN: "OpenTherm version ventilation/heat-recovery",
633
- DIR: READ_ONLY,
634
- VAL: F8_8,
635
- VAR: "VHOpenThermVersion",
636
- },
637
- 0x4C: { # 76, VH Product Type/Version
638
- EN: "Version & type ventilation/heat-recovery",
639
- DIR: READ_ONLY,
640
- VAL: U8,
641
- VAR: {HB: "VHProductType", LB: "VHProductVersion"},
642
- },
643
- 0x4D: { # 77, Relative Ventilation
644
- EN: "Relative ventilation",
645
- DIR: READ_ONLY,
646
- VAL: U8,
647
- VAR: {HB: "RelativeVentilation"},
648
- },
649
- 0x4E: { # 78, Relative Humidity
650
- EN: "Relative humidity",
651
- NL: "Luchtvochtigheid",
652
- DIR: READ_WRITE,
653
- VAL: U8,
654
- VAR: {HB: "RelativeHumidity"},
655
- SENSOR: Sensor.HUMIDITY,
656
- },
657
- 0x4F: { # 79, CO2 Level
658
- EN: "CO2 level",
659
- NL: "CO2 niveau",
660
- DIR: READ_WRITE,
661
- VAL: U16, # 0-2000 ppm
662
- VAR: "CO2Level",
663
- SENSOR: Sensor.CO2_LEVEL,
664
- },
665
- 0x50: { # 80, Supply Inlet Temperature
666
- EN: "Supply inlet temperature",
667
- DIR: READ_ONLY,
668
- VAL: F8_8,
669
- VAR: "SupplyInletTemperature",
670
- SENSOR: Sensor.TEMPERATURE,
671
- },
672
- 0x51: { # 81, Supply Outlet Temperature
673
- EN: "Supply outlet temperature",
674
- DIR: READ_ONLY,
675
- VAL: F8_8,
676
- VAR: "SupplyOutletTemperature",
677
- SENSOR: Sensor.TEMPERATURE,
678
- },
679
- 0x52: { # 82, Exhaust Inlet Temperature
680
- EN: "Exhaust inlet temperature",
681
- DIR: READ_ONLY,
682
- VAL: F8_8,
683
- VAR: "ExhaustInletTemperature",
684
- SENSOR: Sensor.TEMPERATURE,
685
- },
686
- 0x53: { # 83, Exhaust Outlet Temperature
687
- EN: "Exhaust outlet temperature",
688
- DIR: READ_ONLY,
689
- VAL: F8_8,
690
- VAR: "ExhaustOutletTemperature",
691
- SENSOR: Sensor.TEMPERATURE,
692
- },
693
- 0x54: { # 84, Exhaust Fan Speed
694
- EN: "Actual exhaust fan speed",
695
- DIR: READ_ONLY,
696
- VAL: U16,
697
- VAR: "ExhaustFanSpeed",
698
- },
699
- 0x55: { # 85, Inlet Fan Speed
700
- EN: "Actual inlet fan speed",
701
- DIR: READ_ONLY,
702
- VAL: U16,
703
- VAR: "InletFanSpeed",
704
- },
705
- 0x56: { # 86, VH Remote Parameter
706
- EN: "Remote parameter settings ventilation/heat-recovery",
707
- DIR: READ_ONLY,
708
- VAL: FLAG8,
709
- VAR: "VHRemoteParameter",
710
- },
711
- 0x57: { # 87, Nominal Ventilation
712
- EN: "Nominal ventilation value",
713
- DIR: READ_WRITE,
714
- VAL: U8,
715
- VAR: "NominalVentilation",
716
- },
717
- 0x58: { # 88, VH TSP Size
718
- EN: "TSP number ventilation/heat-recovery",
719
- DIR: READ_ONLY,
720
- VAL: U8,
721
- VAR: {HB: "VHTSPSize"},
722
- },
723
- 0x59: { # 89, VH TSP Entry
724
- EN: "TSP entry ventilation/heat-recovery",
725
- DIR: READ_WRITE,
726
- VAL: U8,
727
- VAR: {HB: "VHTSPIndex", LB: "VHTSPValue"},
728
- },
729
- 0x5A: { # 90, VH FHB Size
730
- EN: "Fault buffer size ventilation/heat-recovery",
731
- DIR: READ_ONLY,
732
- VAL: U8,
733
- VAR: {HB: "VHFHBSize"},
734
- },
735
- 0x5B: { # 91, VH FHB Entry
736
- EN: "Fault buffer entry ventilation/heat-recovery",
737
- DIR: READ_ONLY,
738
- VAL: U8,
739
- VAR: {HB: "VHFHBIndex", LB: "VHFHBValue"},
740
- },
741
- # OpenTherm 2.2 IDs
742
- 0x64: { # 100, Remote Override Function
743
- EN: "Remote override function",
744
- DIR: READ_ONLY,
745
- VAL: {HB: FLAG8, LB: U8},
746
- VAR: {HB: "RemoteOverrideFunction"},
747
- },
748
- 0x73: { # 115, OEM Diagnostic code
749
- EN: "OEM diagnostic code",
750
- DIR: READ_ONLY,
751
- VAL: U16,
752
- VAR: "OEMDiagnosticCode",
753
- },
754
- 0x74: { # 116, Starts Burner
755
- EN: "Number of starts burner",
756
- DIR: READ_WRITE,
757
- VAL: U16,
758
- VAR: "StartsBurner",
759
- SENSOR: Sensor.COUNTER,
760
- },
761
- 0x75: { # 117, Starts CH Pump
762
- EN: "Number of starts central heating pump",
763
- DIR: READ_WRITE,
764
- VAL: U16,
765
- VAR: "StartsCHPump",
766
- SENSOR: Sensor.COUNTER,
767
- },
768
- 0x76: { # 118, Starts DHW Pump
769
- EN: "Number of starts DHW pump/valve",
770
- DIR: READ_WRITE,
771
- VAL: U16,
772
- VAR: "StartsDHWPump",
773
- SENSOR: Sensor.COUNTER,
774
- },
775
- 0x77: { # 119, Starts Burner DHW
776
- EN: "Number of starts burner during DHW mode",
777
- DIR: READ_WRITE,
778
- VAL: U16,
779
- VAR: "StartsBurnerDHW",
780
- SENSOR: Sensor.COUNTER,
781
- },
782
- 0x78: { # 120, Hours Burner
783
- EN: "Number of hours burner is in operation (i.e. flame on)",
784
- DIR: READ_WRITE,
785
- VAL: U16,
786
- VAR: "HoursBurner",
787
- SENSOR: Sensor.COUNTER,
788
- },
789
- 0x79: { # 121, Hours CH Pump
790
- EN: "Number of hours central heating pump has been running",
791
- DIR: READ_WRITE,
792
- VAL: U16,
793
- VAR: "HoursCHPump",
794
- SENSOR: Sensor.COUNTER,
795
- },
796
- 0x7A: { # 122, Hours DHW Pump
797
- EN: "Number of hours DHW pump has been running/valve has been opened",
798
- DIR: READ_WRITE,
799
- VAL: U16,
800
- VAR: "HoursDHWPump",
801
- SENSOR: Sensor.COUNTER,
802
- },
803
- 0x7B: { # 123, Hours DHW Burner
804
- EN: "Number of hours DHW burner is in operation during DHW mode",
805
- DIR: READ_WRITE,
806
- VAL: U16,
807
- VAR: "HoursDHWBurner",
808
- SENSOR: Sensor.COUNTER,
809
- },
810
- 0x7C: { # 124, Master OpenTherm Version
811
- EN: "Opentherm version Master",
812
- DIR: WRITE_ONLY,
813
- VAL: F8_8,
814
- VAR: "MasterOpenThermVersion",
815
- },
816
- 0x7D: { # 125, Slave OpenTherm Version
817
- EN: "Opentherm version Slave",
818
- DIR: READ_ONLY,
819
- VAL: F8_8,
820
- VAR: "SlaveOpenThermVersion",
821
- },
822
- 0x7E: { # 126, Master Product Type/Version
823
- EN: "Master product version and type",
824
- DIR: WRITE_ONLY,
825
- VAL: U8,
826
- VAR: {HB: "MasterProductType", LB: "MasterProductVersion"},
827
- },
828
- 0x7F: { # 127, Slave Product Type/Version
829
- EN: "Slave product version and type",
830
- DIR: READ_ONLY,
831
- VAL: U8,
832
- VAR: {HB: "SlaveProductType", LB: "SlaveProductVersion"},
833
- },
834
- # ZX-DAVB extras
835
- 0x71: { # 113, Bad Starts Burner
836
- EN: "Number of un-successful burner starts",
837
- DIR: READ_WRITE,
838
- VAL: U16,
839
- VAR: "BadStartsBurner?",
840
- SENSOR: Sensor.COUNTER,
841
- },
842
- 0x72: { # 114, Low Signals Flame
843
- EN: "Number of times flame signal was too low",
844
- DIR: READ_WRITE,
845
- VAL: U16,
846
- VAR: "LowSignalsFlame?",
847
- SENSOR: Sensor.COUNTER,
848
- },
849
- # https://www.domoticaforum.eu/viewtopic.php?f=70&t=10893
850
- # 0x23: { # 35, Boiler Fan Speed (rpm/60?)?
851
- # },
852
- 0x24: { # 36, Electrical current through burner flame (µA)
853
- EN: "Electrical current through burner flame (µA)",
854
- DIR: READ_ONLY,
855
- VAL: F8_8,
856
- VAR: "BurnerCurrent",
857
- SENSOR: Sensor.CURRENT,
858
- },
859
- 0x25: { # 37, CH2 Room Temperature
860
- EN: "Room temperature for 2nd CH circuit",
861
- DIR: READ_ONLY,
862
- VAL: F8_8,
863
- VAR: "CH2CurrentTemperature",
864
- SENSOR: Sensor.TEMPERATURE,
865
- },
866
- 0x26: { # 38, Relative Humidity, c.f. 0x4E
867
- EN: "Relative humidity",
868
- DIR: READ_ONLY,
869
- VAL: U8,
870
- VAR: {HB: "RelativeHumidity"}, # TODO: or LB?
871
- SENSOR: Sensor.HUMIDITY,
872
- },
873
- },
874
- }
875
- OPENTHERM_MESSAGES: dict = OPENTHERM_SCHEMA[MESSAGES]
876
-
877
- # R8810A 1018 v4: https://www.opentherm.eu/request-details/?post_ids=2944
878
- # as at: 2021/06/28
879
-
880
- # see also: http://otgw.tclcode.com/matrix.cgi#boilers
881
- # 0, 1, 3, 5, 9, 14, 16-19, 24-28, 56-57, 63, 128, 255
882
- # 0x00, 0x01, 0x03, 0x05, 0x09, 0x0E, 0x10-13, 0x18-1C, 0x38-39, 0x3F, 0x80, 0xFF
883
-
884
- # personal testing:
885
- # 0, 1, 3, 5, 6, 12-14, 17-18, 25-26, 28, 56
886
- # 0x00, 0x03, 0x05, 0x06, 0x0C-0D, 0x11-12, 0x19-1A, 0x1C, 0x30-31, 0x38, 0x7D
887
-
888
- OTB_MSG_IDS: dict = {
889
- # These are in the R8810A specification...
890
- 0x00: "Master/Slave status flags",
891
- # 000:HB0: Master status: CH enable
892
- # 000:HB1: Master status: DHW enable
893
- # 000:HB2: Master status: Cooling enable
894
- # 000:HB3: Master status: OTC active
895
- # 000:HB5: Master status: Summer/winter mode
896
- # 000:HB6: Master status: DHW blocking
897
- # 000:LB0: Slave Status: Fault indication
898
- # 000:LB1: Slave Status: CH mode
899
- # 000:LB2: Slave Status: DHW mode
900
- # 000:LB3: Slave Status: Flame status
901
- 0x01: "CH water temperature Setpoint (°C)",
902
- # 001: Control Setpoint i.e. CH water temperature Setpoint (°C)
903
- 0x02: "Master configuration",
904
- # 002:HB0: Master configuration: Smart power
905
- # 002:LB: Master MemberID code
906
- 0x03: "Slave configuration",
907
- # 003:HB0: Slave configuration: DHW present
908
- # 003:HB1: Slave configuration: Control type
909
- # 003:HB4: Slave configuration: Master low-off & pump control
910
- 0x05: "Fault flags & OEM codes",
911
- # 005:HB0: Service request
912
- # 005:HB1: Lockout-reset
913
- # 005:HB2: Low water pressure
914
- # 005:HB3: Gas/flame fault
915
- # 005:HB4: Air pressure fault
916
- # 005:HB5: Water over-temperature
917
- # 005:LB: OEM fault code
918
- 0x06: "Remote boiler parameter flags",
919
- # 006:HB0: Remote boiler parameter transfer-enable: DHW setpoint
920
- # 006:HB1: Remote boiler parameter transfer-enable: max. CH setpoint
921
- # 006:LB0: Remote boiler parameter read/write: DHW setpoint
922
- # 006:LB1: Remote boiler parameter read/write: max. CH setpoint
923
- 0x09: "Remote override room Setpoint", # 009: # c.f. 0x64, 100 # TODO
924
- 0x0A: "Number of TSPs supported by slave", # 010: # TODO
925
- 0x0C: "Size of FHB supported by slave", # 012: # TODO
926
- 0x0E: "Maximum relative modulation level setting (%)", # 014:
927
- 0x10: "Room Setpoint (°C)", # 016:
928
- 0x11: "Relative Modulation Level (%)", # 017:
929
- 0x12: "Water pressure in CH circuit (bar)", # 018:
930
- 0x13: "Water flow rate in DHW circuit. (L/min)", # 019:
931
- 0x18: "Room temperature (°C)", # 024:
932
- 0x19: "Boiler flow water temperature (°C)", # 025:
933
- 0x1A: "DHW temperature (°C)", # 026:
934
- 0x1B: "Outside temperature (°C)", # 027:
935
- 0x1C: "Return water temperature (°C)", # 028:
936
- 0x30: "DHW Setpoint upper & lower bounds for adjustment (°C)", # 048:
937
- 0x31: "Max CH water Setpoint upper & lower bounds for adjustment (°C)", # 049:
938
- 0x38: "DHW Setpoint (°C) (Remote parameter 1)", # 056:
939
- 0x39: "Max CH water Setpoint (°C) (Remote parameters 2)", # 057:
940
- 0x7E: "Master product version number and type", # 126:
941
- 0x7F: "Slave product version number and type", # 127:
942
- #
943
- # These are not in the R8810A spec, but are natively RQ'd by 01:/30:...
944
- # (0[35F]|1[1239AC]|3[89]|7[123456789ABF])
945
- 0x0D: "FHB Entry", # 013: # TODO
946
- 0x73: "OEM diagnostic code", # 115:
947
- 0x7C: "Opentherm version Master", # 124:
948
- 0x7D: "Opentherm version Slave", # 125:
949
- #
950
- # These also have been seen natively RQ'd by 01:/30...
951
- 0x0F: "Max. boiler capacity (kW) and modulation level setting (%)", # 15
952
- 0x71: "Number of un-successful burner starts", # 113
953
- 0x72: "Number of times flame signal was too low", # 114
954
- 0x74: "Number of starts burner", # 116
955
- 0x75: "Number of starts central heating pump", # 117
956
- 0x76: "Number of starts DHW pump/valve", # 118
957
- 0x77: "Number of starts burner during DHW mode", # 119
958
- 0x78: "Number of hours burner is in operation (i.e. flame on)", # 120
959
- 0x79: "Number of hours central heating pump has been running", # 121
960
- 0x7A: "Number of hours DHW pump has been running/valve has been opened", # 122
961
- 0x7B: "Number of hours DHW burner is in operation during DHW mode", # 123
962
- }
963
-
964
-
965
- def parity(x: int) -> int:
966
- """Make this the docstring."""
967
- shiftamount = 1
968
- while x >> shiftamount:
969
- x ^= x >> shiftamount
970
- shiftamount <<= 1
971
- return x & 1
972
-
973
-
974
- def msg_value(val_seqx: str, val_type: str) -> None | float | int | list | str:
975
- """Make this the docstring."""
976
-
977
- # based upon: https://github.com/mvn23/pyotgw/blob/master/pyotgw/protocol.py
978
-
979
- def flag8(byte: str, *args) -> list:
980
- """Split a byte (as a str) into a list of 8 bits.
981
-
982
- In the original payload (the OT specification), the lsb is bit 0 (the last bit),
983
- so the order of bits is reversed here, giving flags[0] (the 1st bit in the
984
- array) as the lsb.
985
- """
986
- return [(bytes.fromhex(byte)[0] & (1 << x)) >> x for x in range(8)]
987
-
988
- def u8(byte: str, *args) -> int:
989
- """Convert a byte (as a str) into an unsigned int."""
990
- return struct.unpack(">B", bytes.fromhex(byte))[0]
991
-
992
- def s8(byte: str, *args) -> int:
993
- """Convert a byte (as a str) into a signed int."""
994
- return struct.unpack(">b", bytes.fromhex(byte))[0]
995
-
996
- def f8_8(high_byte: str, low_byte: str) -> float:
997
- """Convert 2 bytes (as strs) into an OpenTherm f8_8 value."""
998
- if high_byte == low_byte == "FF": # TODO: move up to parser?
999
- raise ValueError()
1000
- return float(s16(high_byte, low_byte) / 256)
1001
-
1002
- def u16(high_byte: str, low_byte: str) -> int:
1003
- """Convert 2 bytes (as strs) into an unsigned int."""
1004
- if high_byte == low_byte == "FF": # TODO: move up to parser?
1005
- raise ValueError()
1006
- buf = struct.pack(">BB", u8(high_byte), u8(low_byte))
1007
- return int(struct.unpack(">H", buf)[0])
1008
-
1009
- def s16(high_byte: str, low_byte: str) -> int:
1010
- """Convert 2 bytes (as strs) into a signed int."""
1011
- if high_byte == low_byte == "FF": # TODO: move up to parser?
1012
- raise ValueError()
1013
- buf = struct.pack(">bB", s8(high_byte), u8(low_byte))
1014
- return int(struct.unpack(">h", buf)[0])
1015
-
1016
- DATA_TYPES: Dict[str, Callable] = {
1017
- FLAG8: flag8,
1018
- U8: u8,
1019
- S8: s8,
1020
- F8_8: f8_8,
1021
- U16: u16,
1022
- S16: s16,
1023
- }
1024
-
1025
- if val_type in DATA_TYPES:
1026
- try:
1027
- return DATA_TYPES[val_type](val_seqx[:2], val_seqx[2:])
1028
- except ValueError:
1029
- return None
1030
- return val_seqx
1031
-
1032
-
1033
- def _decode_flags(frame: str, data_id: int) -> dict:
1034
- try:
1035
- flag_schema = OPENTHERM_SCHEMA[OPENTHERM_MESSAGES[data_id][FLAGS]]
1036
- except KeyError:
1037
- raise KeyError(
1038
- f"Invalid data-id: 0x{data_id:02X} ({data_id}): data-id has no flags"
1039
- )
1040
-
1041
- return flag_schema
1042
-
1043
-
1044
- def decode_frame(frame: str) -> tuple[str, int, dict, dict]:
1045
- if not isinstance(frame, str) or len(frame) != 8:
1046
- raise TypeError(f"Invalid frame (type or length): {frame}")
1047
-
1048
- if int(frame[:2], 16) // 0x80 != parity(int(frame, 16) & 0x7FFFFFFF):
1049
- raise ValueError(f"Invalid parity bit: 0b{int(frame[:2], 16) // 0x80}")
1050
-
1051
- if int(frame[:2], 16) & 0x0F != 0x00:
1052
- raise ValueError(f"Invalid spare bits: 0b{int(frame[:2], 16) & 0x0F:04b}")
1053
-
1054
- msg_type = (int(frame[:2], 16) & 0x70) >> 4
1055
-
1056
- # if msg_type == 0b011: # NOTE: this msg-type may no longer be reserved (R8820?)
1057
- # raise ValueError(f"Reserved msg-type (0b{msg_type:03b})")
1058
-
1059
- data_id = int(frame[2:4], 16)
1060
- msg_schema = OPENTHERM_MESSAGES.get(data_id, {})
1061
-
1062
- # There are five msg_id with FLAGS - the following is not 100% correct...
1063
- data_value = {MSG_NAME: msg_schema.get(FLAGS, msg_schema.get(VAR))}
1064
-
1065
- if msg_type in (0b000, 0b010, 0b011, 0b110, 0b111):
1066
- # if frame[4:] != "0000": # NOTE: this is not a hard rule, even for 0b000
1067
- # raise ValueError(f"Invalid data-value for msg-type: 0x{frame[4:]}")
1068
- return OPENTHERM_MSG_TYPE[msg_type], data_id, data_value, msg_schema
1069
-
1070
- if not msg_schema: # may be a corrupt payload
1071
- data_value[VALUE] = msg_value(frame[4:8], U16)
1072
-
1073
- elif isinstance(msg_schema[VAL], dict):
1074
- data_value[VALUE_HB] = msg_value(
1075
- frame[4:6], msg_schema[VAL].get(HB, msg_schema[VAL])
1076
- )
1077
- data_value[VALUE_LB] = msg_value(
1078
- frame[6:8], msg_schema[VAL].get(LB, msg_schema[VAL])
1079
- )
1080
-
1081
- if msg_schema[VAL].get(HB) == msg_schema[VAL].get(LB) == FLAG8: # msg_id 0 only
1082
- data_value = {VALUE: data_value[VALUE_HB] + data_value[VALUE_LB]}
1083
-
1084
- elif isinstance(msg_schema.get(VAR), dict):
1085
- data_value[VALUE_HB] = msg_value(frame[4:6], msg_schema[VAL])
1086
- data_value[VALUE_LB] = msg_value(frame[6:8], msg_schema[VAL])
1087
-
1088
- elif msg_schema[VAL] in (FLAG8, U8, S8):
1089
- data_value[VALUE] = msg_value(frame[4:6], msg_schema[VAL])
1090
-
1091
- elif msg_schema[VAL] in (S16, U16):
1092
- data_value[VALUE] = msg_value(frame[4:8], msg_schema[VAL])
1093
-
1094
- elif msg_schema[VAL] != F8_8: # shouldn't reach here
1095
- data_value[VALUE] = msg_value(frame[4:8], U16)
1096
-
1097
- elif msg_schema[VAL] == F8_8: # TODO: needs finishing
1098
- result: float = msg_value(frame[4:8], msg_schema[VAL]) # type: ignore[assignment]
1099
- if result is None:
1100
- data_value[VALUE] = result
1101
- elif msg_schema.get(SENSOR) == Sensor.PERCENTAGE:
1102
- # NOTE: OT defines % as 0.0-100.0, but (this) ramses uses 0.0-1.0 elsewhere
1103
- data_value[VALUE] = int(result * 2) / 200 # seems precision of 1%
1104
- elif msg_schema.get(SENSOR) == Sensor.FLOW_RATE:
1105
- data_value[VALUE] = int(result * 100) / 100
1106
- elif msg_schema.get(SENSOR) == Sensor.PRESSURE:
1107
- data_value[VALUE] = int(result * 10) / 10
1108
- else: # if msg_schema.get(SENSOR) == (Sensor.TEMPERATURE, Sensor.HUMIDITY):
1109
- data_value[VALUE] = int(result * 100) / 100
1110
-
1111
- return OPENTHERM_MSG_TYPE[msg_type], data_id, data_value, msg_schema
1112
-
1113
-
1114
- # assert not [
1115
- # k
1116
- # for k, v in OPENTHERM_MESSAGES.items()
1117
- # if not isinstance(v[VAL], dict)
1118
- # and not isinstance(v.get(VAR), dict)
1119
- # and v[VAL] not in msg_value.DATA_TYPES
1120
- # ], "Corrupt OPENTHERM_MESSAGES schema"
1121
-
1122
-
1123
- # https://github.com/rvdbreemen/OTGW-firmware/blob/main/Specification/New%20OT%20data-ids.txt # noqa: E501
1124
-
1125
- """
1126
- New OT Data-ID's - Found two new ID's at this device description:
1127
- http://www.opentherm.eu/product/view/18/feeling-d201-ot
1128
- ID 98: For a specific RF sensor the RF strength and battery level is written
1129
- ID 99: Operating Mode HC1, HC2/ Operating Mode DHW
1130
-
1131
- Found new data-id's at this page:
1132
- https://www.opentherm.eu/request-details/?post_ids=1833
1133
- ID 109: Electricity producer starts
1134
- ID 110: Electricity producer hours
1135
- ID 111: Electricity production
1136
- ID 112: Cumulative Electricity production
1137
-
1138
- Found new Data-ID's at this page:
1139
- https://www.opentherm.eu/request-details/?post_ids=1833
1140
- ID 36: {f8.8} "Electrical current through burner flame" (µA)
1141
- ID 37: {f8.8} "Room temperature for 2nd CH circuit"
1142
- ID 38: {u8 u8} "Relative Humidity"
1143
-
1144
- For Data-ID's 37 and 38 I assumed their data types, for Data ID 36 I determined
1145
- it by matching qSense value with the correct data-type.
1146
-
1147
- I also analysed OT Remeha qSense <-> Remeha Tzerra communication.
1148
- ID 131: {u8 u8} "Remeha dF-/dU-codes"
1149
- ID 132: {u8 u8} "Remeha Service message"
1150
- ID 133: {u8 u8} "Remeha detection connected SCUs"
1151
-
1152
- "Remeha dF-/dU-codes": Should match the dF-/dU-codes written on boiler nameplate.
1153
- Read-Data Request (0 0) returns the data. Also accepts Write-Data Requests (dF
1154
- dU),this returns the boiler to its factory defaults.
1155
-
1156
- "Remeha Service message" Read-Data Request (0 0), boiler returns (0 2) in case of no
1157
- boiler service. Write-Data Request (1 255) clears the boiler service message.
1158
- boiler returns (1 1) = next service type is "A"
1159
- boiler returns (1 2) = next service type is "B"
1160
- boiler returns (1 3) = next service type is "C"
1161
-
1162
- "Remeha detection connected SCUs": Write-Data Request (255 1) enables detection of
1163
- connected SCU prints, correct response is (Write-Ack 255 1).
1164
-
1165
- Other Remeha info:
1166
- ID 5: correponds with the Remeha E:xx fault codes
1167
- ID 11: correponds with the Remeha Pxx parameter codes
1168
- ID 35: reported value is fan speed in rpm/60
1169
- ID 115: correponds with Remeha Status & Sub-status numbers, {u8 u8} data-type
1170
- """