Skip to content

0418: System Fault Log

David Bonnes edited this page Jan 23, 2020 · 18 revisions

When a fault occurs, the controller will send an I packet:

071  I --- 01:145038 --:------ 01:145038 0418 022 000000B00606040000001714359AFFFFFF700012E296
071  I --- 01:145038 --:------ 01:145038 0418 022 000000B00AFA05000000BB1375FCFFFFFF7000000002

The controller will respond to an RQ with a log entry.

Payload decode:
Unknown [0:2] fixed value 0x00
Entry Type [2:4] 00=Fault, 0x40=Restore
Entry Number [4:6] Fault log entry number
Unknown [6:8] fixed value 0xB0
Error Type [8:10] 0x04=Battery Low, 0x06=Comms Fault, ...
zone_idx/domain_id [10:12] (0-0xA, 0xFC etc)
device class [12:14] 0x04=Actuator, 0x01=Sensor
unknown [14:18] fixed value 0x0000
datetime [18:30]
unknown [30:38] fixed value 0xFFFF7000
device_id [38:44]

Python Code

STATE_MAP = {
    "00": "Fault  ",
    "40": "Restore",
    "C0": "Unknown (C0)"
}
DEVICE_CLASS_MAP = {
    "00": "Controller?",
    "01": "Sensor",
    "04": "Actuator",
    "05": "DhwController?"
}
FAULT_TYPE_MAP = {
    "04": "BatteryLow",
    "06": "CommsFault",
    "0A": "SensorError"
}

@parser_decorator
def parser_0418(payload) -> dict:
    def _timestamp(seqx):
        """In the controller UI: YYYY-MM-DD HH:MM"""
        _seqx = int(seqx, 16)
        return dt(
            year=(_seqx & 0b1111111 << 24) >> 24,
            month=(_seqx & 0b1111 << 36) >> 36,
            day=(_seqx & 0b11111 << 31) >> 31,
            hour=(_seqx & 0b11111 << 19) >> 19,
            minute=(_seqx & 0b111111 << 13) >> 13,
            second=(_seqx & 0b111111 << 7) >> 7,
        ).strftime("%Y-%m-%d %H:%M:%S")

    if payload == "000000B0000000000000000000007FFFFF7000000000":
        return {"log_idx": None}  # a null log entry, (or: payload[38:] == "000000")

    assert len(payload) / 2 == 22
    assert payload[:2] == "00"  # unknown_0
    assert payload[2:4] in list(STATE_MAP)
    assert int(payload[4:6], 16) <= 63
    assert payload[6:8] == "B0"  # unknown_1
    assert payload[8:10] in list(FAULT_TYPE_MAP)
    assert int(payload[10:12], 16) <= 11 or payload[10:12] in ["FA"]
    assert payload[12:14] in list(DEVICE_CLASS_MAP)
    assert payload[14:18] == "0000"  # unknown_2
    assert payload[28:30] in ["7F", "FF"]  # last bit in dt field
    assert payload[30:38] == "FFFF7000"  # unknown_3

    return {
        "state": STATE_MAP.get(payload[2:4], payload[2:4]),
        "timestamp": _timestamp(payload[18:30]),
        "fault_type": FAULT_TYPE_MAP.get(payload[8:10], payload[8:10]),
        "zone_idx" if int(payload[10:12], 16) <= 11 else "domain_id": payload[10:12],
        "device_class": DEVICE_CLASS_MAP.get(payload[12:14], payload[12:14]),
        "device_id": dev_hex_to_id(payload[38:]),  # is "00:000001/2 for CTL?
        "log_idx": int(payload[4:6], 16),
    }
Clone this wiki locally