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