PRE-M8-T01: add WarmteLink P1 telegram parser

This commit is contained in:
2026-08-22 17:47:30 +02:00
parent c37dfacfc7
commit 22faeb45bb
4 changed files with 290 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
/ISk5\2MT382-1000
1-3:0.2.8(50)
0-0:1.0.0(240822120000S)
0-0:96.1.1(TEST-GATEWAY)
0-1:24.1.0(006)
0-1:96.1.0(TEST-DHW)
0-1:24.2.1(240822120000S)(5.900*m3)
0-2:24.1.0(012)
0-2:96.1.0(TEST-HEAT)
0-2:24.2.1(240822120000S)(0.017*GJ)
!D18A
+11
View File
@@ -0,0 +1,11 @@
)TU)2NWA-MYRSKY
0-2:24.2.1(240822120000S)(0.017*GJ)
0-1:96.1.0(KAM-REDACTED)
0-0:1.0.0(240822120000S)
0-2:24.1.0(012)
0-1:24.2.1(240822120000S)(5.900*m3)
0-0:96.1.1(WARMTE-REDACTED)
0-1:24.1.0(006)
0-2:96.1.0(KAM-REDACTED)
1-3:0.2.8(50)
!x7z?
+76
View File
@@ -0,0 +1,76 @@
from decimal import Decimal
from pathlib import Path
from scripts.p1_probe import IntegrityStatus, TelegramFramer, parse_telegram
FIXTURES = Path(__file__).parent / "fixtures"
def _fixture(name: str) -> bytes:
return (FIXTURES / name).read_bytes()
def test_standard_dsmr_fixture_has_a_valid_frame_and_crc():
frame = _fixture("dsmr_p1_valid.txt")
framer = TelegramFramer()
assert framer.feed(frame[:23]) == []
assert framer.feed(frame[23:]) == [frame]
telegram = parse_telegram(frame)
assert telegram.integrity is IntegrityStatus.VALID
assert telegram.header == b"/ISk5\\2MT382-1000"
assert telegram.timestamp == "240822120000S"
def test_warmtelink_fixture_is_unverifiable_but_parses_channels_by_obis_code():
telegram = parse_telegram(_fixture("warmtelink_p1_7n1.txt"))
assert telegram.integrity is IntegrityStatus.UNVERIFIABLE
assert "missing standard" in telegram.integrity_reason
assert [channel.number for channel in telegram.channels] == [1, 2]
values = {
channel.number: [(field.value, field.unit) for field in channel.readings]
for channel in telegram.channels
}
assert values[1] == [(Decimal("5.900"), "m3")]
assert values[2] == [(Decimal("0.017"), "GJ")]
def test_arbitrary_chunk_boundaries_and_field_order_do_not_change_parsing():
frame = _fixture("warmtelink_p1_7n1.txt")
framer = TelegramFramer()
frames = []
for byte in frame:
frames.extend(framer.feed(bytes([byte])))
assert frames == [frame]
assert parse_telegram(frames[0]).channels == parse_telegram(frame).channels
def test_unknown_fields_are_preserved_verbatim():
frame = b")HEADER\n9-9:9.9.9(opaque)(still-opaque)\n!nope\n"
telegram = parse_telegram(frame)
assert len(telegram.fields) == 1
assert telegram.fields[0].code == "9-9:9.9.9"
assert telegram.fields[0].raw_values == ("opaque", "still-opaque")
assert telegram.fields[0].value is None
def test_missing_header_and_non_hex_footer_are_unverifiable():
telegram = parse_telegram(b")HEADER\n0-0:1.0.0(240822120000S)\n!zzzz\n")
assert telegram.integrity is IntegrityStatus.UNVERIFIABLE
assert telegram.footer == b"zzzz"
def test_crc_mismatch_is_invalid_when_standard_framing_is_present():
frame = _fixture("dsmr_p1_valid.txt")
invalid = frame[:-5] + b"0000\n"
telegram = parse_telegram(invalid)
assert telegram.integrity is IntegrityStatus.INVALID