From 5fa679c1f286b235cdd98d2b1adadaef5c46bbe4 Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Sat, 16 Nov 2024 21:22:13 +0000 Subject: [PATCH] Fix configuration --- .../versatile_thermostat/config_flow.py | 54 ++++++++++++++++++- .../versatile_thermostat/const.py | 5 ++ .../versatile_thermostat/strings.json | 3 +- .../thermostat_climate.py | 2 +- .../versatile_thermostat/translations/en.json | 3 +- .../versatile_thermostat/translations/fr.json | 3 +- 6 files changed, 64 insertions(+), 6 deletions(-) diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index 8ec00e1..6441d65 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -162,7 +162,26 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): if COMES_FROM in self._infos: del self._infos[COMES_FROM] - async def validate_input(self, data: dict) -> None: + def check_sonoff_trvzb_nb_entities(self, data: dict) -> bool: + """Check the number of entities for Sonoff TRVZB""" + ret = True + if ( + self._infos.get(CONF_SONOFF_TRZB_MODE) + and data.get(CONF_OFFSET_CALIBRATION_LIST) is not None + ): + nb_unders = len(self._infos.get(CONF_UNDERLYING_LIST)) + nb_offset = len(data.get(CONF_OFFSET_CALIBRATION_LIST)) + nb_opening = len(data.get(CONF_OPENING_DEGREE_LIST)) + nb_closing = len(data.get(CONF_CLOSING_DEGREE_LIST)) + if ( + nb_unders != nb_offset + or nb_unders != nb_opening + or nb_unders != nb_closing + ): + ret = False + return ret + + async def validate_input(self, data: dict, step_id) -> None: """Validate the user input allows us to connect. Data has the keys from STEP_*_DATA_SCHEMA with values provided by the user. @@ -178,6 +197,9 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): CONF_POWER_SENSOR, CONF_MAX_POWER_SENSOR, CONF_PRESENCE_SENSOR, + CONF_OFFSET_CALIBRATION_LIST, + CONF_OPENING_DEGREE_LIST, + CONF_CLOSING_DEGREE_LIST, ]: d = data.get(conf, None) # pylint: disable=invalid-name if not isinstance(d, list): @@ -235,6 +257,11 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): except ServiceConfigurationError as err: raise ServiceConfigurationError(conf) from err + # Check that the number of offet_calibration and opening_degree and closing_degree are equals + # to the number of underlying entities + if not self.check_sonoff_trvzb_nb_entities(data): + raise SonoffTRVZBNbEntitiesIncorrect() + def check_config_complete(self, infos) -> bool: """True if the config is now complete (ie all mandatory attributes are set)""" is_central_config = ( @@ -330,6 +357,9 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): ): return False + if not self.check_sonoff_trvzb_nb_entities(infos): + return False + return True def merge_user_input(self, data_schema: vol.Schema, user_input: dict): @@ -359,7 +389,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): if user_input is not None: defaults.update(user_input or {}) try: - await self.validate_input(user_input) + await self.validate_input(user_input, step_id) except UnknownEntity as err: errors[str(err)] = "unknown_entity" except WindowOpenDetectionMethod as err: @@ -370,6 +400,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): errors[str(err)] = "service_configuration_format" except ConfigurationNotCompleteError as err: errors["base"] = "configuration_not_complete" + except SonoffTRVZBNbEntitiesIncorrect as err: + errors["base"] = "sonoff_trvzb_nb_entities_incorrect" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" @@ -528,6 +560,24 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): """Handle the Type flow steps""" _LOGGER.debug("Into ConfigFlow.async_step_type user_input=%s", user_input) + if ( + self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CLIMATE + and user_input is not None + and not user_input.get(CONF_SONOFF_TRZB_MODE) + ): + # Remove TPI info + for key in [ + PROPORTIONAL_FUNCTION_TPI, + CONF_PROP_FUNCTION, + CONF_TPI_COEF_INT, + CONF_TPI_COEF_EXT, + CONF_OFFSET_CALIBRATION_LIST, + CONF_OPENING_DEGREE_LIST, + CONF_CLOSING_DEGREE_LIST, + ]: + if self._infos.get(key): + del self._infos[key] + if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_SWITCH: return await self.generic_step( "type", STEP_THERMOSTAT_SWITCH, user_input, self.async_step_menu diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index e798989..57cc1c6 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -511,6 +511,11 @@ class ConfigurationNotCompleteError(HomeAssistantError): """Error the configuration is not complete""" +class SonoffTRVZBNbEntitiesIncorrect(HomeAssistantError): + """Error to indicate there is an error in the configuration of the Sonoff TRVZB. + The number of specific entities is incorrect.""" + + class overrides: # pylint: disable=invalid-name """An annotation to inform overrides""" diff --git a/custom_components/versatile_thermostat/strings.json b/custom_components/versatile_thermostat/strings.json index 8020c70..66bd390 100644 --- a/custom_components/versatile_thermostat/strings.json +++ b/custom_components/versatile_thermostat/strings.json @@ -487,7 +487,8 @@ "unknown_entity": "Unknown entity id", "window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both", "no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it.", - "service_configuration_format": "The format of the service configuration is wrong" + "service_configuration_format": "The format of the service configuration is wrong", + "sonoff_trvzb_nb_entities_incorrect": "The number of specific entities for Sonoff TRVZB should be equal to the number of underlyings" }, "abort": { "already_configured": "Device is already configured" diff --git a/custom_components/versatile_thermostat/thermostat_climate.py b/custom_components/versatile_thermostat/thermostat_climate.py index b5e43af..bc9977f 100644 --- a/custom_components/versatile_thermostat/thermostat_climate.py +++ b/custom_components/versatile_thermostat/thermostat_climate.py @@ -966,7 +966,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): if not continu: return ret else: - _LOGGER.debug("%s - auto start/stop is disabled") + _LOGGER.debug("%s - auto start/stop is disabled", self) # Continue the normal async_control_heating diff --git a/custom_components/versatile_thermostat/translations/en.json b/custom_components/versatile_thermostat/translations/en.json index 8020c70..66bd390 100644 --- a/custom_components/versatile_thermostat/translations/en.json +++ b/custom_components/versatile_thermostat/translations/en.json @@ -487,7 +487,8 @@ "unknown_entity": "Unknown entity id", "window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both", "no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it.", - "service_configuration_format": "The format of the service configuration is wrong" + "service_configuration_format": "The format of the service configuration is wrong", + "sonoff_trvzb_nb_entities_incorrect": "The number of specific entities for Sonoff TRVZB should be equal to the number of underlyings" }, "abort": { "already_configured": "Device is already configured" diff --git a/custom_components/versatile_thermostat/translations/fr.json b/custom_components/versatile_thermostat/translations/fr.json index f0588cb..8d636db 100644 --- a/custom_components/versatile_thermostat/translations/fr.json +++ b/custom_components/versatile_thermostat/translations/fr.json @@ -481,7 +481,8 @@ "unknown_entity": "entity id inconnu", "window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux.", "no_central_config": "Vous ne pouvez pas cocher 'Utiliser la configuration centrale' car aucune configuration centrale n'a été trouvée. Vous devez créer un Versatile Thermostat de type 'Central Configuration' pour pouvoir l'utiliser.", - "service_configuration_format": "Mauvais format de la configuration du service" + "service_configuration_format": "Mauvais format de la configuration du service", + "sonoff_trvzb_nb_entities_incorrect": "Le nombre d'entités spécifiques au Sonoff TRVZB doit être égal au nombre d'entité sous-jacentes" }, "abort": { "already_configured": "Le device est déjà configuré"