From b07910d384786a4f6439944c0b68e0860e61f82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Matheson=20Wergeland?= Date: Tue, 26 Sep 2023 22:26:30 +0200 Subject: [PATCH] Manage week profiles (#34) * Version 1.7.0 --------- Co-authored-by: Helge Brands --- README.md | 3 ++ pynobo.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b0db90..2caae69 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ These functions send commands to the hub. * async_send_command - Send a list of command string(s) to the hub * async_create_override - Override hub/zones/components * async_update_zone - Update the name, week profile, temperature or override allowing for a zone. +* async_add_week_profile - Create a week profile +* async_update_week_profile - Update a week profile +* async_remove_week_profile - Remove a week profile ### Dictionary helper functions diff --git a/pynobo.py b/pynobo.py index c7a3676..60ea615 100644 --- a/pynobo.py +++ b/pynobo.py @@ -189,6 +189,24 @@ def validate_temperature(temperature: str): if temperature_int > 30: raise ValueError(f'Max temperature is 30°C') + def validate_week_profile(profile): + if type(profile) != list: + raise ValueError("Week profile must be a list") + day_count=0 + for i in profile: + if len(i) != 5: + raise ValueError(f"Invalid week profile entry: {i}") + time = datetime.datetime.strptime(i[0:4], "%H%M") + if not time.minute % 15 == 0: + raise ValueError(f"Week profile entry not in whole quarters: {i}") + # Last character is state (0=Eco, 1=Comfort, 2=Away, 4=Off) + if not i[4] in "0124": + raise ValueError(f"Week profile entry contains invalid state, must be 0, 1, 2, or 4: {i}") + if time.hour == 0 and time.minute == 0: + day_count+=1 + if day_count != 7: + raise ValueError("Week profile must contain exactly 7 entries for midnight (starting with 0000)") + class Model: """ @@ -916,6 +934,81 @@ async def async_update_zone(self, zone_id, name=None, week_profile_id=None, temp await self.async_send_command(command) + + async def async_add_week_profile(self, name, profile=None): + """ + Add the name and profile parameter for a week. + + :param name: the new zone name + :param profile: the new profile (default None) + """ + + # if no profile is defined + if profile is None: + profile=['00000','12001','16000','00000','12001','16000','00000','12001','16000','00000','12001','16000','00000','12001','16000','00000','12001','16000','00000','12001','16000'] + _LOGGER.debug('profile: %s', ",".join(profile)) + nobo.API.validate_week_profile(profile) + + # profile id is decided by the hub + week_profile_id='0' + converted_profile =','.join(profile) + name = name.replace(" ", "\u00A0") + if len(name.encode('utf-8')) > 100: + raise ValueError(f'Zone name "{name}" too long (max 100 bytes when encoded as UTF-8)') + + command = [nobo.API.ADD_WEEK_PROFILE] + [week_profile_id] + [name] + [converted_profile] + + await self.async_send_command(command) + + + async def async_update_week_profile(self, week_profile_id: str, name=None, profile=None): + """ + Update the name and profile parameter for a week. + + :param week_profile_id: the week_profile_id + :param name: the new zone name (default None) + :param profile: the new profile (default None) + """ + + if week_profile_id not in self.week_profiles: + raise ValueError(f"Unknown week profile {week_profile_id}") + if name is None and profile is None: + raise ValueError("Set at least name or profile to update") + + if name: + name = name.replace(" ", "\u00A0") + if len(name.encode('utf-8')) > 100: + raise ValueError(f'Zone name "{name}" too long (max 100 bytes when encoded as UTF-8)') + else: + name = self.week_profiles[week_profile_id]["name"] + + if profile: + nobo.API.validate_week_profile(profile) + else: + profile = self.week_profiles[week_profile_id]["profile"] + + command = [nobo.API.UPDATE_WEEK_PROFILE, week_profile_id, name, ','.join(profile)] + await self.async_send_command(command) + + async def async_remove_week_profile(self, week_profile_id: str): + """ + Remove the week profile. + + :param week_profile_id: the week_profile_id + """ + + if week_profile_id not in self.week_profiles: + raise ValueError(f"Unknown week profile {week_profile_id}") + + if week_profile_id in (v['week_profile_id'] for k, v in self.zones.items()): + raise ValueError(f"Week profile {week_profile_id} in use, can not remove") + + name = self.week_profiles[week_profile_id]["name"] + profile = self.week_profiles[week_profile_id]["profile"] + + command = [nobo.API.REMOVE_WEEK_PROFILE, week_profile_id, name, ','.join(profile)] + await self.async_send_command(command) + def get_week_profile_status(self, week_profile_id, dt=datetime.datetime.today()): """ Get the status of a week profile at a certain time in the week. Monday is day 0. diff --git a/setup.py b/setup.py index 6ef558c..5605860 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ # For a discussion on single-sourcing the version across setup.py and the # project code, see # https://packaging.python.org/en/latest/single_source_version.html - version='1.6.1', + version='1.7.0', description='Nobø Hub / Nobø Energy Control TCP/IP Interface', license='GPLv3+',