From 003acfea263fb4714593096e3b4772eb43ee4e05 Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Tue, 28 Jan 2025 18:52:09 +0000 Subject: [PATCH] Test input in config_flow ok --- .../versatile_thermostat/config_flow.py | 31 +++++++++++++++++++ .../versatile_thermostat/const.py | 4 +++ .../versatile_thermostat/translations/fr.json | 10 +++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index 2ba7621..def65d1 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any +import re import logging import copy from collections.abc import Mapping # pylint: disable=import-error @@ -273,6 +274,34 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): CONF_MIN_OPENING_DEGREES ) from exc + # Check the VSWITCH configuration. There should be the same number of vswitch_on (resp. vswitch_off) than the number of underlying entity + if self._infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_SWITCH and step_id == "type": + if not self.check_vswitch_configuration(data): + raise VirtualSwitchConfigurationIncorrect(CONF_VSWITCH_ON_CMD_LIST) + + def check_vswitch_configuration(self, data) -> bool: + """Check the Virtual switch configuration and return True if the configuration is correct""" + nb_under = len(data.get(CONF_UNDERLYING_LIST, [])) + + # check format of each command_on + for command in data.get(CONF_VSWITCH_ON_CMD_LIST, []) + data.get(CONF_VSWITCH_OFF_CMD_LIST, []): + pattern = r"^(?P[a-zA-Z0-9_]+)(?:/(?P[a-zA-Z0-9_]+)(?::(?P[a-zA-Z0-9_]+))?)?$" + if not re.match(pattern, command): + return False + + nb_command_on = len(data.get(CONF_VSWITCH_ON_CMD_LIST, [])) + nb_command_off = len(data.get(CONF_VSWITCH_OFF_CMD_LIST, [])) + if (nb_command_on == nb_under or nb_command_on == 0) and (nb_command_off == nb_under or nb_command_off == 0): + # There is enough command_on and off + # Check if one under is not a switch (which have default command). + if any( + not thermostat_type.startswith(SWITCH_DOMAIN) and not thermostat_type.startswith(INPUT_BOOLEAN_DOMAIN) for thermostat_type in data.get(CONF_UNDERLYING_LIST, []) + ): + if nb_command_on != nb_under or nb_command_off != nb_under: + return False + return True + return False + def check_config_complete(self, infos) -> bool: """True if the config is now complete (ie all mandatory attributes are set)""" is_central_config = ( @@ -407,6 +436,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler): errors["base"] = "valve_regulation_nb_entities_incorrect" except ValveRegulationMinOpeningDegreesIncorrect as err: errors[str(err)] = "min_opening_degrees_format" + except VirtualSwitchConfigurationIncorrect as err: + errors["base"] = "vswitch_configuration_incorrect" except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected exception") errors["base"] = "unknown" diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index 5d44768..501f4d3 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -565,6 +565,10 @@ class ValveRegulationMinOpeningDegreesIncorrect(HomeAssistantError): """Error to indicate that the minimal opening degrees is not a list of int separated by coma""" +class VirtualSwitchConfigurationIncorrect(HomeAssistantError): + """Error when a virtual switch is not configured correctly""" + + class overrides: # pylint: disable=invalid-name """An annotation to inform overrides""" diff --git a/custom_components/versatile_thermostat/translations/fr.json b/custom_components/versatile_thermostat/translations/fr.json index 467d78a..a98e1a8 100644 --- a/custom_components/versatile_thermostat/translations/fr.json +++ b/custom_components/versatile_thermostat/translations/fr.json @@ -336,8 +336,9 @@ "auto_regulation_use_device_temp": "Compenser la température interne du sous-jacent", "inverse_switch_command": "Inverser la commande", "auto_fan_mode": " Auto ventilation mode", - "on_command_text": "Personnalisation des commandes", + "on_command_text": "Personnalisation des commandes d'allumage", "vswitch_on_command": "Commande d'allumage (optionnel)", + "off_command_text": "Personnalisation des commandes d'extinction", "vswitch_off_command": "Commande d'extinction (optionnel)" }, "data_description": { @@ -351,9 +352,7 @@ "auto_regulation_use_device_temp": "Compenser la temperature interne du sous-jacent pour accélérer l'auto-régulation", "inverse_switch_command": "Inverse la commande du switch pour une installation avec fil pilote et diode", "auto_fan_mode": "Active la ventilation automatiquement en cas d'écart important", - "on_command_text": "Pour les sous-jacents de type `select` ou `climate`", - "vswitch_on_command": "Une liste de commande d'allumage au format: action:parameter. Exemple: select_option:comfort. Cf. README pour plus d'exmples", - "vswitch_off_command": "Une liste de commande d'extinction au format: action:parameter. Exemple: select_option:frost. Cf. README pour plus d'exmples" + "on_command_text": "Pour les sous-jacents de type `select` ou `climate`" } }, "tpi": { @@ -498,7 +497,8 @@ "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", "valve_regulation_nb_entities_incorrect": "Le nombre d'entités pour la régulation par vanne doit être égal au nombre d'entité sous-jacentes", - "min_opening_degrees_format": "Une liste d'entiers positifs séparés par des ',' est attendu. Exemple : 20, 25, 30" + "min_opening_degrees_format": "Une liste d'entiers positifs séparés par des ',' est attendu. Exemple : 20, 25, 30", + "vswitch_configuration_incorrect": "La configuration de la personnalisation des commandes est incorrecte. Elle est obligatoire pour les sous-jacents non switch et le format doit être 'service_name[/attribut:valeur]'. Plus d'informations dans le README." }, "abort": { "already_configured": "Le device est déjà configuré"