From 6c543dc915b13311e2139bfb7f10b314e3294f71 Mon Sep 17 00:00:00 2001 From: gdluck <9041273+gdluck@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:55:07 +0000 Subject: [PATCH] fix(heating): name the central unit status WHATs 22, 23, 24, 30 and 31 OWNd logged *4*30*#0## and *4*31*#0## as "mode is unknown". The Legrand WHO 4 specification v2.0.0 lists them as central unit status reports (p. 5, p. 24, p. 64): 22 at least one probe OFF, 23 at least one probe in protection, 24 at least one probe in manual, 30 failure discovered, 31 central unit battery KO. libqtdevices TS10_1_0_23 names 30 and 31 (thermal_device.cpp:47-48, MALFUNCTIONING_FOUND and BATTERY_KO) and ignores them for the mode (:214-217). No behaviour change: mode stays None, only the log text is specific. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_018oHp1P4vxxoFnaLUUZpbpZ --- OWNd/message/heating.py | 16 +++++++++++++ tests/test_who4_central_status_whats.py | 32 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/test_who4_central_status_whats.py diff --git a/OWNd/message/heating.py b/OWNd/message/heating.py index a9fda25..22b5539 100644 --- a/OWNd/message/heating.py +++ b/OWNd/message/heating.py @@ -250,6 +250,22 @@ def __init__(self, data: str) -> None: self._human_readable_log = ( f"Zone {self._zone}'s remote control is enabled" ) + elif self._mode in (22, 23, 24, 30, 31): + # Central unit system status (Legrand WHO 4 p. 5, p. 64): + # 22 at least one probe OFF, 23 at least one probe in + # protection, 24 at least one probe in manual, 30 failure + # discovered, 31 central unit battery KO. + self._mode_name = None + _status_text = { + 22: "at least one probe is OFF", + 23: "at least one probe is in protection", + 24: "at least one probe is in manual mode", + 30: "a failure was discovered", + 31: "the central unit battery is KO", + }[self._mode] + self._human_readable_log = ( + f"Zone {self._zone}'s central unit reports {_status_text}" + ) else: self._mode_name = None self._human_readable_log = f"Zone {self._zone}'s mode is unknown" diff --git a/tests/test_who4_central_status_whats.py b/tests/test_who4_central_status_whats.py new file mode 100644 index 0000000..e1b9a22 --- /dev/null +++ b/tests/test_who4_central_status_whats.py @@ -0,0 +1,32 @@ +"""WHO 4 WHAT 22/23/24/30/31 are central-unit status reports (Legrand WHO 4 +v2.0.0 p. 5, p. 24, p. 64; libqtdevices TS10_1_0_23 thermal_device.cpp:47-48 +MALFUNCTIONING_FOUND = 30, BATTERY_KO = 31).""" + +import pytest + +from OWNd.message import OWNEvent, OWNHeatingEvent + + +@pytest.mark.parametrize( + ("what", "text"), + [ + (22, "at least one probe is OFF"), + (23, "at least one probe is in protection"), + (24, "at least one probe is in manual mode"), + (30, "a failure was discovered"), + (31, "the central unit battery is KO"), + ], +) +def test_central_unit_status_whats_are_named(what: int, text: str): + msg = OWNEvent.parse(f"*4*{what}*#0##") + assert isinstance(msg, OWNHeatingEvent) + assert msg.mode is None + assert msg.message_type != "hvac_mode" + assert text in msg.human_readable_log + assert "unknown" not in msg.human_readable_log + + +def test_other_unknown_whats_still_log_unknown(): + msg = OWNEvent.parse("*4*999*1##") + assert msg.mode is None + assert "unknown" in msg.human_readable_log