From fde13967ecb252fca9e3329680b85917f2de62e5 Mon Sep 17 00:00:00 2001 From: SukramJ Date: Sat, 12 Oct 2024 19:59:44 +0200 Subject: [PATCH 1/2] Improve profile validation --- changelog.md | 5 +- hahomematic/platforms/custom/climate.py | 22 +++--- pyproject.toml | 2 +- tests/test_climate.py | 98 +++++++++++++++++++++++-- 4 files changed, 109 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 69408b79..4abbacfc 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,10 @@ +# Version 2024.10.7 (2024-10-12) + +- Improve profile validation + # Version 2024.10.6 (2024-10-11) - Export SIMPLE_PROFILE_DICT, SIMPLE_WEEKDAY_LIST -- # Version 2024.10.5 (2024-10-11) diff --git a/hahomematic/platforms/custom/climate.py b/hahomematic/platforms/custom/climate.py index 9587c31f..92104c83 100644 --- a/hahomematic/platforms/custom/climate.py +++ b/hahomematic/platforms/custom/climate.py @@ -41,7 +41,7 @@ _PARTY_INIT_DATE: Final = "2000_01_01 00:00" _RAW_SCHEDULE_DICT = dict[str, float | int] _TEMP_CELSIUS: Final = "°C" -SCHEDULE_SLOT_RANGE: Final = range(1, 14) +SCHEDULE_SLOT_RANGE: Final = range(1, 13) SCHEDULE_TIME_RANGE: Final = range(1441) HM_PRESET_MODE_PREFIX: Final = "week_program_" @@ -456,7 +456,7 @@ def _validate_and_convert_simple_to_profile_weekday( """Convert simple weekday list to weekday dict.""" if not self.min_temp <= base_temperature <= self.max_temp: raise ValidationException( - f"VALIDATE_SIMPLE_PROFILE: Base temperature {base_temperature} not in valid range (min: {self.min_temp}, " + f"VALIDATE_PROFILE: Base temperature {base_temperature} not in valid range (min: {self.min_temp}, " f"max: {self.max_temp})" ) @@ -468,22 +468,22 @@ def _validate_and_convert_simple_to_profile_weekday( slot_no = 1 for slot in sorted_simple_weekday_list: if (starttime := slot.get(ScheduleSlotType.STARTTIME)) is None: - raise ValidationException("VALIDATE_SIMPLE_PROFILE: STARTTIME is missing.") + raise ValidationException("VALIDATE_PROFILE: STARTTIME is missing.") if (endtime := slot.get(ScheduleSlotType.ENDTIME)) is None: - raise ValidationException("VALIDATE_SIMPLE_PROFILE: ENDTIME is missing.") + raise ValidationException("VALIDATE_PROFILE: ENDTIME is missing.") if (temperature := slot.get(ScheduleSlotType.TEMPERATURE)) is None: - raise ValidationException("VALIDATE_SIMPLE_PROFILE: TEMPERATURE is missing.") + raise ValidationException("VALIDATE_PROFILE: TEMPERATURE is missing.") if _convert_time_str_to_minutes(str(starttime)) < _convert_time_str_to_minutes( previous_endtime ): raise ValidationException( - f"VALIDATE_SIMPLE_PROFILE: Timespans are overlapping with a previous slot for starttime: {starttime} / endtime: {endtime}" + f"VALIDATE_PROFILE: Timespans are overlapping with a previous slot for starttime: {starttime} / endtime: {endtime}" ) if not self.min_temp <= float(temperature) <= self.max_temp: raise ValidationException( - f"VALIDATE_SIMPLE_PROFILE: Temperature {temperature} not in valid range (min: {self.min_temp}, " + f"VALIDATE_PROFILE: Temperature {temperature} not in valid range (min: {self.min_temp}, " f"max: {self.max_temp}) for starttime: {starttime} / endtime: {endtime}" ) @@ -520,7 +520,11 @@ def _validate_profile_weekday( ) -> None: """Validate the profile weekday.""" previous_endtime = 0 - for no in SCHEDULE_SLOT_RANGE: + if len(weekday_data) != 13: + raise ValidationException( + f"VALIDATE_PROFILE: {"Too many" if len(weekday_data) > 13 else "Too few"} slots in profile: {profile} / weekday: {weekday}" + ) + for no in range(1, 13): if no not in weekday_data: raise ValidationException( f"VALIDATE_PROFILE: slot no {no} is missing in profile: {profile} / weekday: {weekday}" @@ -942,7 +946,7 @@ def _sort_simple_weekday_list(simple_weekday_list: SIMPLE_WEEKDAY_LIST) -> SIMPL def _fillup_weekday_data(base_temperature: float, weekday_data: WEEKDAY_DICT) -> WEEKDAY_DICT: """Fillup weekday data.""" - for slot_no in SCHEDULE_SLOT_RANGE: + for slot_no in range(1, 14): if slot_no not in weekday_data: weekday_data[slot_no] = { ScheduleSlotType.ENDTIME: "24:00", diff --git a/pyproject.toml b/pyproject.toml index 9abb5bfd..9f5496e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "hahomematic" -version = "2024.10.6" +version = "2024.10.7" license = {text = "MIT License"} description = "Homematic interface for Home Assistant running on Python 3." readme = "README.md" diff --git a/tests/test_climate.py b/tests/test_climate.py index 55329011..b2abaa20 100644 --- a/tests/test_climate.py +++ b/tests/test_climate.py @@ -675,13 +675,16 @@ async def test_climate_ip_with_pydevccu(central_unit_mini) -> None: profile="P1", base_temperature=16.0, simple_profile_data={ - "MONDAY": [], - "TUESDAY": [], - "WEDNESDAY": [], - "THURSDAY": [], - "FRIDAY": [], - "SATURDAY": [], - "SUNDAY": [], + "MONDAY": [ + {"TEMPERATURE": 17.0, "STARTTIME": "05:00", "ENDTIME": "06:00"}, + {"TEMPERATURE": 22.0, "STARTTIME": "19:00", "ENDTIME": "22:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "09:00", "ENDTIME": "15:00"}, + ], + "TUESDAY": [ + {"TEMPERATURE": 17.0, "STARTTIME": "05:00", "ENDTIME": "06:00"}, + {"TEMPERATURE": 22.0, "STARTTIME": "19:00", "ENDTIME": "22:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "09:00", "ENDTIME": "15:00"}, + ], }, ) @@ -692,3 +695,84 @@ async def test_climate_ip_with_pydevccu(central_unit_mini) -> None: "MONDAY": [], }, ) + + manual_simple_weekday_list3 = [ + {"TEMPERATURE": 17.0, "STARTTIME": "05:00", "ENDTIME": "06:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "06:00", "ENDTIME": "07:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "07:00", "ENDTIME": "08:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "08:00", "ENDTIME": "09:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "09:00", "ENDTIME": "10:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "10:00", "ENDTIME": "11:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "11:00", "ENDTIME": "12:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "12:00", "ENDTIME": "13:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "13:00", "ENDTIME": "14:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "14:00", "ENDTIME": "15:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "15:00", "ENDTIME": "16:00"}, + ] + weekday_data3 = climate_bwth._validate_and_convert_simple_to_profile_weekday( + base_temperature=16.0, simple_weekday_list=manual_simple_weekday_list3 + ) + assert weekday_data3 == { + 1: {"ENDTIME": "05:00", "TEMPERATURE": 16.0}, + 2: {"ENDTIME": "06:00", "TEMPERATURE": 17.0}, + 3: {"ENDTIME": "07:00", "TEMPERATURE": 17.0}, + 4: {"ENDTIME": "08:00", "TEMPERATURE": 17.0}, + 5: {"ENDTIME": "09:00", "TEMPERATURE": 17.0}, + 6: {"ENDTIME": "10:00", "TEMPERATURE": 17.0}, + 7: {"ENDTIME": "11:00", "TEMPERATURE": 17.0}, + 8: {"ENDTIME": "12:00", "TEMPERATURE": 17.0}, + 9: {"ENDTIME": "13:00", "TEMPERATURE": 17.0}, + 10: {"ENDTIME": "14:00", "TEMPERATURE": 17.0}, + 11: {"ENDTIME": "15:00", "TEMPERATURE": 17.0}, + 12: {"ENDTIME": "16:00", "TEMPERATURE": 17.0}, + 13: {"ENDTIME": "24:00", "TEMPERATURE": 16.0}, + } + await climate_bwth.set_simple_profile_weekday( + profile="P1", + weekday="MONDAY", + base_temperature=16.0, + simple_weekday_list=manual_simple_weekday_list3, + ) + + await climate_bwth.set_simple_profile_weekday( + profile="P1", + weekday="MONDAY", + base_temperature=16.0, + simple_weekday_list=[ + {"TEMPERATURE": 17.0, "STARTTIME": "05:00", "ENDTIME": "06:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "06:00", "ENDTIME": "07:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "13:00", "ENDTIME": "14:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "14:00", "ENDTIME": "15:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "15:00", "ENDTIME": "16:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "12:00", "ENDTIME": "13:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "07:00", "ENDTIME": "08:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "08:00", "ENDTIME": "09:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "10:00", "ENDTIME": "11:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "11:00", "ENDTIME": "12:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "09:00", "ENDTIME": "10:00"}, + ], + ) + + # 14 entries + with pytest.raises(ValidationException): + await climate_bwth.set_simple_profile_weekday( + profile="P1", + weekday="MONDAY", + base_temperature=16.0, + simple_weekday_list=[ + {"TEMPERATURE": 17.0, "STARTTIME": "05:00", "ENDTIME": "06:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "06:00", "ENDTIME": "07:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "07:00", "ENDTIME": "08:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "08:00", "ENDTIME": "09:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "09:00", "ENDTIME": "10:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "10:00", "ENDTIME": "11:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "11:00", "ENDTIME": "12:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "12:00", "ENDTIME": "13:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "13:00", "ENDTIME": "14:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "14:00", "ENDTIME": "15:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "15:00", "ENDTIME": "16:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "16:00", "ENDTIME": "17:00"}, + {"TEMPERATURE": 22.0, "STARTTIME": "17:00", "ENDTIME": "18:00"}, + {"TEMPERATURE": 17.0, "STARTTIME": "18:00", "ENDTIME": "19:00"}, + ], + ) From 768123861e6e4e6186b01a423b8fa182b3449900 Mon Sep 17 00:00:00 2001 From: SukramJ Date: Sat, 12 Oct 2024 20:02:30 +0200 Subject: [PATCH 2/2] Update climate.py --- hahomematic/platforms/custom/climate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hahomematic/platforms/custom/climate.py b/hahomematic/platforms/custom/climate.py index 92104c83..7309fbd3 100644 --- a/hahomematic/platforms/custom/climate.py +++ b/hahomematic/platforms/custom/climate.py @@ -42,6 +42,7 @@ _RAW_SCHEDULE_DICT = dict[str, float | int] _TEMP_CELSIUS: Final = "°C" SCHEDULE_SLOT_RANGE: Final = range(1, 13) +SCHEDULE_SLOT_IN_RANGE: Final = range(1, 14) SCHEDULE_TIME_RANGE: Final = range(1441) HM_PRESET_MODE_PREFIX: Final = "week_program_" @@ -524,7 +525,7 @@ def _validate_profile_weekday( raise ValidationException( f"VALIDATE_PROFILE: {"Too many" if len(weekday_data) > 13 else "Too few"} slots in profile: {profile} / weekday: {weekday}" ) - for no in range(1, 13): + for no in SCHEDULE_SLOT_RANGE: if no not in weekday_data: raise ValidationException( f"VALIDATE_PROFILE: slot no {no} is missing in profile: {profile} / weekday: {weekday}" @@ -946,7 +947,7 @@ def _sort_simple_weekday_list(simple_weekday_list: SIMPLE_WEEKDAY_LIST) -> SIMPL def _fillup_weekday_data(base_temperature: float, weekday_data: WEEKDAY_DICT) -> WEEKDAY_DICT: """Fillup weekday data.""" - for slot_no in range(1, 14): + for slot_no in SCHEDULE_SLOT_IN_RANGE: if slot_no not in weekday_data: weekday_data[slot_no] = { ScheduleSlotType.ENDTIME: "24:00",