Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions OWNd/message/heating.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -211,19 +212,29 @@ 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}'"
)
elif (
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}'"
)
Expand All @@ -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}'"
)
Expand Down Expand Up @@ -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."""
Expand Down
23 changes: 20 additions & 3 deletions tests/test_message_edges.py
Original file line number Diff line number Diff line change
Expand Up @@ -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##")
Expand Down
Loading