diff --git a/OWNd/message/heating.py b/OWNd/message/heating.py index a9fda25..6c92328 100644 --- a/OWNd/message/heating.py +++ b/OWNd/message/heating.py @@ -186,6 +186,7 @@ def __init__(self, data: str) -> None: self._holiday_end_date: tuple[int, int, int] | None = None self._holiday_end_time: tuple[int, int] | None = None self._manual_timed_duration: tuple[int, int] | None = None + self._holiday_days: int | None = None self._is_active = None self._is_heating = None @@ -211,9 +212,14 @@ def __init__(self, data: str) -> None: self._mode in [0, 210, 211, 212, 215] or (self._mode >= 2101 and self._mode <= 2103) or (self._mode >= 2201 and self._mode <= 2216) + or (self._mode >= 23001 and self._mode <= 23255) ): + # 23xxx: holiday days in conditioning mode (Legrand WHO 4 + # p. 5 / p. 64; libqtdevices thermal_device.cpp:58, 258-262). self._type = MESSAGE_TYPE_MODE self._mode_name = CLIMATE_MODE_COOL + if self._mode >= 23001: + self._holiday_days = self._mode % 1000 self._human_readable_log = ( f"Zone {self._zone}'s mode is set to '{self._mode_name}'" ) @@ -221,9 +227,14 @@ def __init__(self, data: str) -> None: self._mode in [1, 110, 111, 112, 115] or (self._mode >= 1101 and self._mode <= 1103) or (self._mode >= 1201 and self._mode <= 1216) + or (self._mode >= 13001 and self._mode <= 13255) ): + # 13xxx: holiday days in heating mode (Legrand WHO 4 p. 5 / + # p. 64; libqtdevices thermal_device.cpp:68, 303-307). self._type = MESSAGE_TYPE_MODE self._mode_name = CLIMATE_MODE_HEAT + if self._mode >= 13001: + self._holiday_days = self._mode % 1000 self._human_readable_log = ( f"Zone {self._zone}'s mode is set to '{self._mode_name}'" ) @@ -232,11 +243,11 @@ def __init__(self, data: str) -> None: or (self._mode >= 3101 and self._mode <= 3116) or (self._mode >= 3201 and self._mode <= 3216) or (self._mode >= 33001 and self._mode <= 33255) - or (self._mode >= 23001 and self._mode <= 23255) - or (self._mode >= 13001 and self._mode <= 13255) ): self._type = MESSAGE_TYPE_MODE self._mode_name = CLIMATE_MODE_AUTO + if self._mode >= 33001: + self._holiday_days = self._mode % 1000 self._human_readable_log = ( f"Zone {self._zone}'s mode is set to '{self._mode_name}'" ) @@ -662,6 +673,11 @@ def holiday_end_date(self) -> tuple[int, int, int] | None: """Holiday / weekend end date (day, month, year), or None.""" return self._holiday_end_date + @property + def holiday_days(self) -> int | None: + """Holiday days (1..255) named by a 13xxx/23xxx/33xxx WHAT, else None.""" + return self._holiday_days + @property def holiday_end_time(self) -> tuple[int, int] | None: """Holiday / weekend end time (hour, minute), or None.""" diff --git a/tests/test_message_edges.py b/tests/test_message_edges.py index 4d3ac4d..03179d1 100644 --- a/tests/test_message_edges.py +++ b/tests/test_message_edges.py @@ -196,15 +196,32 @@ def test_mode_auto_variant_315(self): assert isinstance(msg, OWNHeatingEvent) assert msg.mode == CLIMATE_MODE_AUTO - def test_mode_auto_weekly_23001(self): + def test_mode_holiday_23001_is_conditioning(self): + # Legrand WHO 4 p. 5 and p. 64: 23xxx = holiday days in conditioning + # mode; libqtdevices thermal_device.cpp:58 (SUM_HOLIDAY = 23000) and + # :258-262 (season SE_SUMMER). msg = OWNEvent.parse("*4*23001*1##") assert isinstance(msg, OWNHeatingEvent) - assert msg.mode == CLIMATE_MODE_AUTO + assert msg.mode == CLIMATE_MODE_COOL + assert msg.holiday_days == 1 - def test_mode_auto_weekly_13001(self): + def test_mode_holiday_13001_is_heating(self): + # Legrand WHO 4 p. 5 and p. 64: 13xxx = holiday days in heating mode; + # libqtdevices thermal_device.cpp:68 (WIN_HOLIDAY = 13000) and + # :303-307 (season SE_WINTER). msg = OWNEvent.parse("*4*13001*1##") assert isinstance(msg, OWNHeatingEvent) + assert msg.mode == CLIMATE_MODE_HEAT + assert msg.holiday_days == 1 + + def test_mode_holiday_33004_is_generic(self): + msg = OWNEvent.parse("*4*33004*#0##") assert msg.mode == CLIMATE_MODE_AUTO + assert msg.holiday_days == 4 + + def test_holiday_days_is_none_for_other_modes(self): + assert OWNEvent.parse("*4*1*1##").holiday_days is None + assert OWNEvent.parse("*4*3101*#0##").holiday_days is None def test_unknown_mode(self): msg = OWNEvent.parse("*4*999*1##")