From a0cdaa8a7fe0bfa2e0c192fe9649990681ea2a36 Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Tue, 4 Feb 2025 14:35:41 +0100 Subject: [PATCH] Issue #872 (#886) * ConfigFlow (translation to change) * +1 * With implemn in thermostat_switch (not finished) * GUI fixe * +1 * Select Working * Test input in config_flow ok * documentation * Add github copilot Add first test ok for UnderlyingSwitch * All tests ok * Fix warnings * Fix All tests ok * Translations * safety * Add quick-start documentation --------- Co-authored-by: Jean-Marc Collin --- .devcontainer/configuration.yaml | 8 +- .devcontainer/devcontainer.json | 4 +- README-fr.md | 58 +++--- README.md | 61 ++++--- .../versatile_thermostat/config_flow.py | 33 +++- .../versatile_thermostat/config_schema.py | 16 +- .../versatile_thermostat/const.py | 7 + .../versatile_thermostat/manifest.json | 2 +- .../versatile_thermostat/strings.json | 66 ++++--- .../versatile_thermostat/thermostat_switch.py | 18 +- .../versatile_thermostat/translations/en.json | 70 ++++---- .../versatile_thermostat/translations/fr.json | 71 +++++--- .../versatile_thermostat/underlyings.py | 88 ++++++--- documentation/en/images/config-vswitch1.png | Bin 0 -> 12358 bytes documentation/en/images/config-vswitch2.png | Bin 0 -> 21527 bytes documentation/en/over-switch.md | 51 +++++- documentation/en/quick-start.md | 84 +++++++++ documentation/en/troubleshooting.md | 10 ++ documentation/fr/images/config-vswitch1.png | Bin 0 -> 34098 bytes documentation/fr/images/config-vswitch2.png | Bin 0 -> 56418 bytes documentation/fr/over-switch.md | 43 ++++- documentation/fr/quick-start.md | 86 +++++++++ documentation/fr/self-regulation.md | 5 +- documentation/fr/troubleshooting.md | 10 ++ images/new-icon.png | Bin 0 -> 6822 bytes tests/test_safety.py | 1 + tests/test_virtual_switch.py | 168 ++++++++++++++++++ 27 files changed, 772 insertions(+), 188 deletions(-) create mode 100644 documentation/en/images/config-vswitch1.png create mode 100644 documentation/en/images/config-vswitch2.png create mode 100644 documentation/en/quick-start.md create mode 100644 documentation/fr/images/config-vswitch1.png create mode 100644 documentation/fr/images/config-vswitch2.png create mode 100644 documentation/fr/quick-start.md create mode 100644 images/new-icon.png create mode 100644 tests/test_virtual_switch.py diff --git a/.devcontainer/configuration.yaml b/.devcontainer/configuration.yaml index 47c7bfd..a2f1332 100644 --- a/.devcontainer/configuration.yaml +++ b/.devcontainer/configuration.yaml @@ -8,12 +8,13 @@ recorder: domains: - input_boolean - input_number + - input_select - switch - climate - sensor - binary_sensor - number - - input_select + - select - versatile_thermostat logger: @@ -243,6 +244,11 @@ climate: heater: input_boolean.fake_valve_sonoff_trvzb2 target_sensor: input_number.fake_temperature_sensor1 ac_mode: false + - platform: generic_thermostat + name: Underlying switch climate + heater: input_boolean.fake_heater_switch2 + target_sensor: input_number.fake_temperature_sensor1 + ac_mode: false input_datetime: fake_last_seen: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 147f824..216abbf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -37,7 +37,9 @@ "yzhang.markdown-all-in-one", "github.vscode-github-actions", "azuretools.vscode-docker", - "huizhou.githd" + "huizhou.githd", + "github.copilot", + "github.copilot-chat" ], "settings": { "files.eol": "\n", diff --git a/README-fr.md b/README-fr.md index b0a55d6..94c2762 100644 --- a/README-fr.md +++ b/README-fr.md @@ -13,15 +13,13 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et une ré # Quoi de neuf ? ![Nouveau](images/new-icon.png) -> * **Release 6.8**: +> * **Release 7.2**: > -> Ajout d'une nouvelle méthode de régulation pour les Versatile Thermostat de type `over_climate`. Cette méthode nommée 'Contrôle direct de la vanne' permet de contrôler directement la vanne d'un TRV et éventuellement un décalage pour calibrer le thermomètre interne de votre TRV. Cette nouvelle méthode a été testée avec des Sonoff TRVZB et généralisée pour d'autre type de TRV pour lesquels la vanne est directement commandable via des entités de type `number`. +> - Prise en compte native des équipements pilotable via une entité de type `select` (ou `input_select`) ou `climate` pour des _VTherm_ de type `over_switch`. Cette évolution rend obsolète, la création de switch virtuels pour l'intégration des Nodon ou Heaty ou eCosy ... etc. Plus d'informations [ici](documentation/fr/over-switch.md#la-personnalisation-des-commandes). > -> Plus d'informations [ici](documentation/fr/over-climate.md) et [ici](documentation/fr/self-regulation.md). +> - Lien vers la documentation : cette version 7.2 expérimente des liens vers la documentation depuis les pages de configuration. Le lien est accessible via l'icone [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/over-switch.md#configuration). Elle est expérimentée sur certaines pages de la configuration. > -> * **Refonte de la documentation**: -> -> Avec toutes les évolutions réalisées depuis le début de l'intégration, la documentation nécessitait une profonde re-organisation, c'est chose faite sur cette version. Tous vos retours sur cette nouvelle organisation seront les bienvenus. +> - Ajout d'un chapitre dans la documentation nommé 'Démarrage rapide' permettant de mettre en oeuvre rapidement un _VTherm_ en fonction de votre équipement. La page est [ici](documentation/quick-start.md) # 🍻 Merci pour les bières [buymecoffee](https://www.buymeacoffee.com/jmcollin78) 🍻 @@ -41,32 +39,38 @@ Un grand merci à tous mes fournisseurs de bières pour leurs dons et leurs enco _PAC_ : Pompe à chaleur + _HA_ : Home Assistant + + _sous-jacent_ : l'équipement controlé par _VTherm_ + + # Documentation La documentation est maintenant découpée en plusieurs pages pour faciliter la lecture et la recherche d'informations : 1. [présentation](documentation/fr/presentation.md), 2. [Installation](documentation/fr/installation.md), -3. [choisir un type de VTherm](documentation/fr/creation.md), -4. [les attributs de base](documentation/fr/base-attributes.md) -5. [configurer un VTherm sur un `switch`](documentation/fr/over-switch.md) -6. [configurer un VTherm sur un `climate`](documentation/fr/over-climate.md) -7. [configurer un VTherm sur une vanne](documentation/fr/over-valve.md) -8. [les pré-régages (preset)](documentation/fr/feature-presets.md) -9. [la gestion des ouvertures](documentation/fr/feature-window.md) -10. [la gestion de la présence](documentation/fr/feature-presence.md) -11. [la gestion de mouvement](documentation/fr/feature-motion.md) -12. [la gestion de la puissance](documentation/fr/feature-power.md) -13. [l'auto start and stop](documentation/fr/feature-auto-start-stop.md) -14. [la contrôle centralisé de tous vos VTherms](documentation/fr/feature-central-mode.md) -15. [la commande du chauffage central](documentation/fr/feature-central-boiler.md) -16. [aspects avancés, mode sécurité](documentation/fr/feature-advanced.md) -17. [l'auto-régulation](documentation/fr/self-regulation.md) -18. [exemples de réglages](documentation/fr/tuning-examples.md) -19. [les différents algorithmes](documentation/fr/algorithms.md) -20. [documentation de référence](documentation/fr/reference.md) -21. [exemple de réglages](documentation/fr/tuning-examples.md) -22. [dépannage](documentation/fr/troubleshooting.md) -23. [notes de version](documentation/fr/releases.md) +3. [Démarrage rapide](documentation/fr/quick-start.md) +4. [choisir un type de VTherm](documentation/fr/creation.md), +5. [les attributs de base](documentation/fr/base-attributes.md) +6. [configurer un VTherm sur un `switch`](documentation/fr/over-switch.md) +7. [configurer un VTherm sur un `climate`](documentation/fr/over-climate.md) +8. [configurer un VTherm sur une vanne](documentation/fr/over-valve.md) +9. [les pré-régages (preset)](documentation/fr/feature-presets.md) +10. [la gestion des ouvertures](documentation/fr/feature-window.md) +11. [la gestion de la présence](documentation/fr/feature-presence.md) +12. [la gestion de mouvement](documentation/fr/feature-motion.md) +13. [la gestion de la puissance](documentation/fr/feature-power.md) +14. [l'auto start and stop](documentation/fr/feature-auto-start-stop.md) +15. [la contrôle centralisé de tous vos VTherms](documentation/fr/feature-central-mode.md) +16. [la commande du chauffage central](documentation/fr/feature-central-boiler.md) +17. [aspects avancés, mode sécurité](documentation/fr/feature-advanced.md) +18. [l'auto-régulation](documentation/fr/self-regulation.md) +19. [exemples de réglages](documentation/fr/tuning-examples.md) +20. [les différents algorithmes](documentation/fr/algorithms.md) +21. [documentation de référence](documentation/fr/reference.md) +22. [exemple de réglages](documentation/fr/tuning-examples.md) +23. [dépannage](documentation/fr/troubleshooting.md) +24. [notes de version](documentation/fr/releases.md) # Quelques résultats diff --git a/README.md b/README.md index 8c7930d..a5e66e2 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,13 @@ This custom component for Home Assistant is an upgrade and a complete rewrite of # What's new? ![New](images/new-icon.png) -> * **Release 6.8**: +# What's New? +![New](images/new-icon.png) +> * **Release 7.2**: > -> Added a new regulation method for `over_climate` type Versatile Thermostats. This method, called 'Direct control of valve', allows direct control of a TRV valve and optionally a calibration offset for the internal thermometer of your TRV. This new method has been tested with Sonoff TRVZB and generalized for other TRV types whose valves can be directly controlled via `number` entities. +> - Native support for devices controlled via a `select` (or `input_select`) or `climate` entity for _VTherm_ of type `over_switch`. This update makes the creation of virtual switches obsolete for integrating Nodon, Heaty, eCosy, etc. More information [here](documentation/en/over-switch.md#command-customization). > -> More information [here](documentation/en/over-climate.md) and [here](documentation/en/self-regulation.md). -> -> * **Documentation overhaul**: -> -> With all the developments since the start of the integration, the documentation needed a major reorganization, which has been completed in this version. All feedback on this new organization is welcome. +> - Documentation links: Version 7.2 introduces experimental links to the documentation from the configuration pages. The link is accessible via the icon [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/over-switch.md#configuration). This feature is currently tested on some configuration pages. # 🍻 Thanks for the beers [buymecoffee](https://www.buymeacoffee.com/jmcollin78) 🍻 A big thank you to all my beer sponsors for their donations and encouragements. It means a lot to me and motivates me to keep going! If this integration has saved you money, buy me a beer in return; I would greatly appreciate it! @@ -38,34 +36,39 @@ A big thank you to all my beer sponsors for their donations and encouragements. _slope_: The slope of the temperature curve, measured in ° (C or K)/h. It is positive when the temperature increases and negative when it decreases. This slope is calculated based on the _EMA_. - _PAC_ : Heat pump + _PAC_: Heat pump + + _HA_: Home Assistant + + _underlying_: the device controlled by _VTherm_ # Documentation The documentation is now divided into several pages for easier reading and searching: 1. [Introduction](documentation/en/presentation.md), 2. [Installation](documentation/en/installation.md), -3. [Choosing a VTherm type](documentation/en/creation.md), -4. [Basic attributes](documentation/en/base-attributes.md) -3. [Configuring a VTherm on a `switch`](documentation/en/over-switch.md) -3. [Configuring a VTherm on a `climate`](documentation/en/over-climate.md) -3. [Configuring a VTherm on a valve](documentation/en/over-valve.md) -4. [Presets](documentation/en/feature-presets.md) -5. [Window management](documentation/en/feature-window.md) -6. [Presence management](documentation/en/feature-presence.md) -7. [Motion management](documentation/en/feature-motion.md) -8. [Power management](documentation/en/feature-power.md) -9. [Auto start and stop](documentation/en/feature-auto-start-stop.md) -10. [Centralized control of all VTherms](documentation/en/feature-central-mode.md) -11. [Central heating control](documentation/en/feature-central-boiler.md) -12. [Advanced aspects, security mode](documentation/en/feature-advanced.md) -12. [Self-regulation](documentation/en/self-regulation.md) -13. [Tuning examples](documentation/en/tuning-examples.md) -14. [Algorithms](documentation/en/algorithms.md) -15. [Reference documentation](documentation/en/reference.md) -16. [Tuning examples](documentation/en/tuning-examples.md) -17. [Troubleshooting](documentation/en/troubleshooting.md) -18. [Release notes](documentation/en/releases.md) +3. [Démarrage rapide](documentation/en/quick-start.md) +4. [Choosing a VTherm type](documentation/en/creation.md), +5. [Basic attributes](documentation/en/base-attributes.md) +6. [Configuring a VTherm on a `switch`](documentation/en/over-switch.md) +7. [Configuring a VTherm on a `climate`](documentation/en/over-climate.md) +8. [Configuring a VTherm on a valve](documentation/en/over-valve.md) +9. [Presets](documentation/en/feature-presets.md) +10. [Window management](documentation/en/feature-window.md) +11. [Presence management](documentation/en/feature-presence.md) +12. [Motion management](documentation/en/feature-motion.md) +13. [Power management](documentation/en/feature-power.md) +14. [Auto start and stop](documentation/en/feature-auto-start-stop.md) +15. [Centralized control of all VTherms](documentation/en/feature-central-mode.md) +16. [Central heating control](documentation/en/feature-central-boiler.md) +17. [Advanced aspects, security mode](documentation/en/feature-advanced.md) +18. [Self-regulation](documentation/en/self-regulation.md) +19. [Tuning examples](documentation/en/tuning-examples.md) +20. [Algorithms](documentation/en/algorithms.md) +21. [Reference documentation](documentation/en/reference.md) +22. [Tuning examples](documentation/en/tuning-examples.md) +23. [Troubleshooting](documentation/en/troubleshooting.md) +24. [Release notes](documentation/en/releases.md) # Some results diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index 2ba7621..40b5153 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -4,13 +4,12 @@ from __future__ import annotations from typing import Any +import re import logging import copy from collections.abc import Mapping # pylint: disable=import-error import voluptuous as vol -from homeassistant.exceptions import HomeAssistantError - from homeassistant.core import callback from homeassistant.config_entries import ( ConfigEntry, @@ -273,6 +272,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 +434,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/config_schema.py b/custom_components/versatile_thermostat/config_schema.py index 1c35830..8f12b98 100644 --- a/custom_components/versatile_thermostat/config_schema.py +++ b/custom_components/versatile_thermostat/config_schema.py @@ -16,6 +16,14 @@ from homeassistant.components.input_number import ( DOMAIN as INPUT_NUMBER_DOMAIN, ) +from homeassistant.components.select import ( + DOMAIN as SELECT_DOMAIN, +) + +from homeassistant.components.input_select import ( + DOMAIN as INPUT_SELECT_DOMAIN, +) + from homeassistant.components.input_datetime import ( DOMAIN as INPUT_DATETIME_DOMAIN, ) @@ -120,9 +128,7 @@ STEP_CENTRAL_BOILER_SCHEMA = vol.Schema( STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name { vol.Required(CONF_UNDERLYING_LIST): selector.EntitySelector( - selector.EntitySelectorConfig( - domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN], multiple=True - ), + selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN, SELECT_DOMAIN, INPUT_SELECT_DOMAIN, CLIMATE_DOMAIN], multiple=True), ), vol.Optional(CONF_HEATER_KEEP_ALIVE): cv.positive_int, vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In( @@ -132,6 +138,10 @@ STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name ), vol.Optional(CONF_AC_MODE, default=False): cv.boolean, vol.Optional(CONF_INVERSE_SWITCH, default=False): cv.boolean, + vol.Optional("on_command_text"): vol.In([]), + vol.Optional(CONF_VSWITCH_ON_CMD_LIST): selector.TextSelector(selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT, multiple=True)), + vol.Optional("off_command_text"): vol.In([]), + vol.Optional(CONF_VSWITCH_OFF_CMD_LIST): selector.TextSelector(selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT, multiple=True)), } ) diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index 745af24..501f4d3 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -127,6 +127,9 @@ CONF_OPENING_DEGREE_LIST = "opening_degree_entity_ids" CONF_CLOSING_DEGREE_LIST = "closing_degree_entity_ids" CONF_MIN_OPENING_DEGREES = "min_opening_degrees" +CONF_VSWITCH_ON_CMD_LIST = "vswitch_on_command" +CONF_VSWITCH_OFF_CMD_LIST = "vswitch_off_command" + # Deprecated CONF_HEATER = "heater_entity_id" CONF_HEATER_2 = "heater_entity2_id" @@ -562,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/manifest.json b/custom_components/versatile_thermostat/manifest.json index 3d8dc4c..04e1756 100644 --- a/custom_components/versatile_thermostat/manifest.json +++ b/custom_components/versatile_thermostat/manifest.json @@ -14,6 +14,6 @@ "quality_scale": "silver", "requirements": [], "ssdp": [], - "version": "7.1.6", + "version": "7.2.0", "zeroconf": [] } \ No newline at end of file diff --git a/custom_components/versatile_thermostat/strings.json b/custom_components/versatile_thermostat/strings.json index dda80a1..989d76c 100644 --- a/custom_components/versatile_thermostat/strings.json +++ b/custom_components/versatile_thermostat/strings.json @@ -35,7 +35,7 @@ }, "main": { "title": "Add new Versatile Thermostat", - "description": "Main mandatory attributes", + "description": "Main mandatory attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/base-attributes.md)", "data": { "name": "Name", "thermostat_type": "Thermostat type", @@ -71,7 +71,7 @@ }, "type": { "title": "Linked entities", - "description": "Linked entities attributes", + "description": "Linked entities attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/over-switch.md#configuration)", "data": { "underlying_entity_ids": "The device(s) to be controlled", "heater_keep_alive": "Switch keep-alive interval in seconds", @@ -82,7 +82,11 @@ "auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_use_device_temp": "Use internal temperature of the underlying", "inverse_switch_command": "Inverse switch command", - "auto_fan_mode": "Auto fan mode" + "auto_fan_mode": "Auto fan mode", + "on_command_text": "Turn on command customization", + "vswitch_on_command": "Optional turn on commands", + "off_command_text": "Turn off command customization", + "vswitch_off_command": "Optional turn off commands" }, "data_description": { "underlying_entity_ids": "The device(s) to be controlled - 1 is required", @@ -94,12 +98,13 @@ "auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", - "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary" + "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary", + "on_command_text": "For underlying of type `select` or `climate` you have to customize the commands." } }, "tpi": { "title": "TPI", - "description": "Time Proportional Integral attributes", + "description": "Time Proportional Integral attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coef_int", "tpi_coef_ext": "coef_ext", @@ -113,14 +118,14 @@ }, "presets": { "title": "Presets", - "description": "Select if the thermostat will use central preset - deselect for the thermostat to have its own presets", + "description": "Select if the thermostat will use central preset - deselect for the thermostat to have its own presets [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presets.md)", "data": { "use_presets_central_config": "Use central presets configuration" } }, "window": { "title": "Window management", - "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease", + "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-window.md)", "data": { "window_sensor_entity_id": "Window sensor entity id", "window_delay": "Window sensor 'on' delay (seconds)", @@ -144,7 +149,7 @@ }, "motion": { "title": "Motion management", - "description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name", + "description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-motion.md)", "data": { "motion_sensor_entity_id": "Motion sensor entity id", "motion_delay": "Activation delay", @@ -164,7 +169,7 @@ }, "power": { "title": "Power management", - "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W).", + "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-power.md)", "data": { "power_sensor_entity_id": "Power", "max_power_sensor_entity_id": "Max power", @@ -180,7 +185,7 @@ }, "presence": { "title": "Presence management", - "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.", + "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presence.md)", "data": { "presence_sensor_entity_id": "Presence sensor", "use_presence_central_config": "Use central presence temperature configuration. Deselect to use specific temperature entities" @@ -191,7 +196,7 @@ }, "advanced": { "title": "Advanced parameters", - "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.", + "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", "safety_delay_min": "Safety delay (in minutes)", @@ -209,7 +214,7 @@ }, "central_boiler": { "title": "Control of the central boiler", - "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Command to turn-on", "central_boiler_deactivation_service": "Command to turn-off" @@ -221,7 +226,7 @@ }, "valve_regulation": { "title": "Self-regulation with valve", - "description": "Configuration for self-regulation with direct control of the valve", + "description": "Configuration for self-regulation with direct control of the valve [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Offset calibration entities", "opening_degree_entity_ids": "Opening degree entities", @@ -283,7 +288,7 @@ }, "main": { "title": "Main - {name}", - "description": "Main mandatory attributes", + "description": "Main mandatory attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/base-attributes.md)", "data": { "name": "Name", "thermostat_type": "Thermostat type", @@ -319,7 +324,7 @@ }, "type": { "title": "Entities - {name}", - "description": "Linked entities attributes", + "description": "Linked entities attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/over-switch.md#configuration)", "data": { "underlying_entity_ids": "The device(s) to be controlled", "heater_keep_alive": "Switch keep-alive interval in seconds", @@ -330,7 +335,11 @@ "auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_use_device_temp": "Use internal temperature of the underlying", "inverse_switch_command": "Inverse switch command", - "auto_fan_mode": "Auto fan mode" + "auto_fan_mode": "Auto fan mode", + "on_command_text": "Turn on command customization", + "vswitch_on_command": "Optional turn on commands", + "off_command_text": "Turn off command customization", + "vswitch_off_command": "Optional turn off commands" }, "data_description": { "underlying_entity_ids": "The device(s) to be controlled - 1 is required", @@ -341,13 +350,14 @@ "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", - "inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command", - "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary" + "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", + "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary", + "on_command_text": "For underlying of type `select` or `climate` you have to customize the commands." } }, "tpi": { "title": "TPI - {name}", - "description": "Time Proportional Integral attributes", + "description": "Time Proportional Integral attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coef_int", "tpi_coef_ext": "coef_ext", @@ -361,14 +371,14 @@ }, "presets": { "title": "Presets - {name}", - "description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities", + "description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presets.md)", "data": { "use_presets_central_config": "Use central presets configuration" } }, "window": { "title": "Window - {name}", - "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease", + "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-window.md)", "data": { "window_sensor_entity_id": "Window sensor entity id", "window_delay": "Window sensor 'on' delay (seconds)", @@ -391,7 +401,7 @@ }, "motion": { "title": "Motion - {name}", - "description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name", + "description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-motion.md)", "data": { "motion_sensor_entity_id": "Motion sensor entity id", "motion_delay": "Activation delay", @@ -411,7 +421,7 @@ }, "power": { "title": "Power - {name}", - "description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).", + "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-power.md)", "data": { "power_sensor_entity_id": "Power", "max_power_sensor_entity_id": "Max power", @@ -427,7 +437,7 @@ }, "presence": { "title": "Presence - {name}", - "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.", + "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presence.md)", "data": { "presence_sensor_entity_id": "Presence sensor", "use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities" @@ -438,7 +448,7 @@ }, "advanced": { "title": "Advanced - {name}", - "description": "Advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.", + "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", "safety_delay_min": "Safety delay (in minutes)", @@ -456,7 +466,7 @@ }, "central_boiler": { "title": "Control of the central boiler - {name}", - "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Command to turn-on", "central_boiler_deactivation_service": "Command to turn-off" @@ -468,7 +478,7 @@ }, "valve_regulation": { "title": "Self-regulation with valve - {name}", - "description": "Configuration for self-regulation with direct control of the valve", + "description": "Configuration for self-regulation with direct control of the valve [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Offset calibration entities", "opening_degree_entity_ids": "Opening degree entities", @@ -559,7 +569,7 @@ "preset_mode": { "state": { "power": "Shedding", - "security": "Safety", + "safety": "Safety", "none": "Manual", "frost": "Frost" } diff --git a/custom_components/versatile_thermostat/thermostat_switch.py b/custom_components/versatile_thermostat/thermostat_switch.py index 9fef124..1c819b4 100644 --- a/custom_components/versatile_thermostat/thermostat_switch.py +++ b/custom_components/versatile_thermostat/thermostat_switch.py @@ -14,6 +14,8 @@ from .const import ( CONF_UNDERLYING_LIST, CONF_HEATER_KEEP_ALIVE, CONF_INVERSE_SWITCH, + CONF_VSWITCH_ON_CMD_LIST, + CONF_VSWITCH_OFF_CMD_LIST, overrides, ) @@ -40,6 +42,8 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): "tpi_coef_ext", "power_percent", "calculated_on_percent", + "vswitch_on_commands", + "vswitch_off_commands", } ) ) @@ -47,6 +51,8 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): def __init__(self, hass: HomeAssistant, unique_id, name, config_entry) -> None: """Initialize the thermostat over switch.""" self._is_inversed: bool | None = None + self._lst_vswitch_on: list[str] = [] + self._lst_vswitch_off: list[str] = [] super().__init__(hass, unique_id, name, config_entry) @property @@ -75,10 +81,16 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): max_on_percent=self._max_on_percent, ) + self._is_inversed = config_entry.get(CONF_INVERSE_SWITCH) is True + lst_switches = config_entry.get(CONF_UNDERLYING_LIST) + self._lst_vswitch_on = config_entry.get(CONF_VSWITCH_ON_CMD_LIST, []) + self._lst_vswitch_off = config_entry.get(CONF_VSWITCH_OFF_CMD_LIST, []) delta_cycle = self._cycle_min * 60 / len(lst_switches) for idx, switch in enumerate(lst_switches): + vswitch_on = self._lst_vswitch_on[idx] if idx < len(self._lst_vswitch_on) else None + vswitch_off = self._lst_vswitch_off[idx] if idx < len(self._lst_vswitch_off) else None self._underlyings.append( UnderlyingSwitch( hass=self._hass, @@ -86,10 +98,11 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): switch_entity_id=switch, initial_delay_sec=idx * delta_cycle, keep_alive_sec=config_entry.get(CONF_HEATER_KEEP_ALIVE, 0), + vswitch_on=vswitch_on, + vswitch_off=vswitch_off, ) ) - self._is_inversed = config_entry.get(CONF_INVERSE_SWITCH) is True self._should_relaunch_control_heating = False @overrides @@ -142,6 +155,9 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): "calculated_on_percent" ] = self._prop_algorithm.calculated_on_percent + self._attr_extra_state_attributes["vswitch_on_commands"] = self._lst_vswitch_on + self._attr_extra_state_attributes["vswitch_off_commands"] = self._lst_vswitch_off + self.async_write_ha_state() _LOGGER.debug( "%s - Calling update_custom_attributes: %s", diff --git a/custom_components/versatile_thermostat/translations/en.json b/custom_components/versatile_thermostat/translations/en.json index bc69bb8..989d76c 100644 --- a/custom_components/versatile_thermostat/translations/en.json +++ b/custom_components/versatile_thermostat/translations/en.json @@ -35,7 +35,7 @@ }, "main": { "title": "Add new Versatile Thermostat", - "description": "Main mandatory attributes", + "description": "Main mandatory attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/base-attributes.md)", "data": { "name": "Name", "thermostat_type": "Thermostat type", @@ -71,7 +71,7 @@ }, "type": { "title": "Linked entities", - "description": "Linked entities attributes", + "description": "Linked entities attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/over-switch.md#configuration)", "data": { "underlying_entity_ids": "The device(s) to be controlled", "heater_keep_alive": "Switch keep-alive interval in seconds", @@ -82,7 +82,11 @@ "auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_use_device_temp": "Use internal temperature of the underlying", "inverse_switch_command": "Inverse switch command", - "auto_fan_mode": "Auto fan mode" + "auto_fan_mode": "Auto fan mode", + "on_command_text": "Turn on command customization", + "vswitch_on_command": "Optional turn on commands", + "off_command_text": "Turn off command customization", + "vswitch_off_command": "Optional turn off commands" }, "data_description": { "underlying_entity_ids": "The device(s) to be controlled - 1 is required", @@ -94,12 +98,13 @@ "auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", - "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary" + "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary", + "on_command_text": "For underlying of type `select` or `climate` you have to customize the commands." } }, "tpi": { "title": "TPI", - "description": "Time Proportional Integral attributes", + "description": "Time Proportional Integral attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coef_int", "tpi_coef_ext": "coef_ext", @@ -113,14 +118,14 @@ }, "presets": { "title": "Presets", - "description": "Select if the thermostat will use central preset - deselect for the thermostat to have its own presets", + "description": "Select if the thermostat will use central preset - deselect for the thermostat to have its own presets [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presets.md)", "data": { "use_presets_central_config": "Use central presets configuration" } }, "window": { "title": "Window management", - "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease", + "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-window.md)", "data": { "window_sensor_entity_id": "Window sensor entity id", "window_delay": "Window sensor 'on' delay (seconds)", @@ -144,7 +149,7 @@ }, "motion": { "title": "Motion management", - "description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name", + "description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-motion.md)", "data": { "motion_sensor_entity_id": "Motion sensor entity id", "motion_delay": "Activation delay", @@ -164,7 +169,7 @@ }, "power": { "title": "Power management", - "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W).", + "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-power.md)", "data": { "power_sensor_entity_id": "Power", "max_power_sensor_entity_id": "Max power", @@ -180,7 +185,7 @@ }, "presence": { "title": "Presence management", - "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.", + "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presence.md)", "data": { "presence_sensor_entity_id": "Presence sensor", "use_presence_central_config": "Use central presence temperature configuration. Deselect to use specific temperature entities" @@ -191,7 +196,7 @@ }, "advanced": { "title": "Advanced parameters", - "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.", + "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", "safety_delay_min": "Safety delay (in minutes)", @@ -209,7 +214,7 @@ }, "central_boiler": { "title": "Control of the central boiler", - "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Command to turn-on", "central_boiler_deactivation_service": "Command to turn-off" @@ -221,7 +226,7 @@ }, "valve_regulation": { "title": "Self-regulation with valve", - "description": "Configuration for self-regulation with direct control of the valve", + "description": "Configuration for self-regulation with direct control of the valve [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Offset calibration entities", "opening_degree_entity_ids": "Opening degree entities", @@ -234,7 +239,7 @@ "opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities", "closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV has the entity for better regulation. There should be one per underlying climate entities", "proportional_function": "Algorithm to use (TPI is the only one for now)", - "min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30" + "min_opening_degrees": "Opening degree minimum value for each underlying device, comma separated. Default to 0. Example: 20, 25, 30" } } }, @@ -283,7 +288,7 @@ }, "main": { "title": "Main - {name}", - "description": "Main mandatory attributes", + "description": "Main mandatory attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/base-attributes.md)", "data": { "name": "Name", "thermostat_type": "Thermostat type", @@ -319,7 +324,7 @@ }, "type": { "title": "Entities - {name}", - "description": "Linked entities attributes", + "description": "Linked entities attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/over-switch.md#configuration)", "data": { "underlying_entity_ids": "The device(s) to be controlled", "heater_keep_alive": "Switch keep-alive interval in seconds", @@ -330,7 +335,11 @@ "auto_regulation_periode_min": "Regulation minimum period", "auto_regulation_use_device_temp": "Use internal temperature of the underlying", "inverse_switch_command": "Inverse switch command", - "auto_fan_mode": "Auto fan mode" + "auto_fan_mode": "Auto fan mode", + "on_command_text": "Turn on command customization", + "vswitch_on_command": "Optional turn on commands", + "off_command_text": "Turn off command customization", + "vswitch_off_command": "Optional turn off commands" }, "data_description": { "underlying_entity_ids": "The device(s) to be controlled - 1 is required", @@ -341,13 +350,14 @@ "auto_regulation_dtemp": "The threshold in ° (or % for valve) under which the temperature change will not be sent", "auto_regulation_periode_min": "Duration in minutes between two regulation update", "auto_regulation_use_device_temp": "Use the eventual internal temperature sensor of the underlying to speedup the self-regulation", - "inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command", - "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary" + "inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command", + "auto_fan_mode": "Automatically activate fan when huge heating/cooling is necessary", + "on_command_text": "For underlying of type `select` or `climate` you have to customize the commands." } }, "tpi": { "title": "TPI - {name}", - "description": "Time Proportional Integral attributes", + "description": "Time Proportional Integral attributes [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coef_int", "tpi_coef_ext": "coef_ext", @@ -361,14 +371,14 @@ }, "presets": { "title": "Presets - {name}", - "description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities", + "description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presets.md)", "data": { "use_presets_central_config": "Use central presets configuration" } }, "window": { "title": "Window - {name}", - "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease", + "description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-window.md)", "data": { "window_sensor_entity_id": "Window sensor entity id", "window_delay": "Window sensor 'on' delay (seconds)", @@ -391,7 +401,7 @@ }, "motion": { "title": "Motion - {name}", - "description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name", + "description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-motion.md)", "data": { "motion_sensor_entity_id": "Motion sensor entity id", "motion_delay": "Activation delay", @@ -411,7 +421,7 @@ }, "power": { "title": "Power - {name}", - "description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).", + "description": "Power management attributes.\nGives the power and max power sensor of your home.\nSpecify the power consumption of the heater when on.\nAll sensors and device power should use the same unit (kW or W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-power.md)", "data": { "power_sensor_entity_id": "Power", "max_power_sensor_entity_id": "Max power", @@ -427,7 +437,7 @@ }, "presence": { "title": "Presence - {name}", - "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.", + "description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-presence.md)", "data": { "presence_sensor_entity_id": "Presence sensor", "use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities" @@ -438,7 +448,7 @@ }, "advanced": { "title": "Advanced - {name}", - "description": "Advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.", + "description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/en/feature-advanced.md)", "data": { "minimal_activation_delay": "Minimum activation delay", "safety_delay_min": "Safety delay (in minutes)", @@ -456,7 +466,7 @@ }, "central_boiler": { "title": "Control of the central boiler - {name}", - "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Enter the services to call to turn on/off the central boiler. Leave blank if no service call is to be made (in this case, you will have to manage the turning on/off of your central boiler yourself). The service called must be formatted as follows: `entity_id/service_name[/attribute:value]` (/attribute:value is optional)\nFor example:\n- to turn on a switch: `switch.controle_chaudiere/switch.turn_on`\n- to turn off a switch: `switch.controle_chaudiere/switch.turn_off`\n- to program the boiler to 25° and thus force its ignition: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- to send 10° to the boiler and thus force its extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Command to turn-on", "central_boiler_deactivation_service": "Command to turn-off" @@ -468,7 +478,7 @@ }, "valve_regulation": { "title": "Self-regulation with valve - {name}", - "description": "Configuration for self-regulation with direct control of the valve", + "description": "Configuration for self-regulation with direct control of the valve [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Offset calibration entities", "opening_degree_entity_ids": "Opening degree entities", @@ -481,7 +491,7 @@ "opening_degree_entity_ids": "The list of the 'opening degree' entities. There should be one per underlying climate entities", "closing_degree_entity_ids": "The list of the 'closing degree' entities. Set it if your TRV has the entity for better regulation. There should be one per underlying climate entities", "proportional_function": "Algorithm to use (TPI is the only one for now)", - "min_opening_degrees": "A comma seperated list of minimal opening degrees. Default to 0. Example: 20, 25, 30" + "min_opening_degrees": "Opening degree minimum value for each underlying device, comma separated. Default to 0. Example: 20, 25, 30" } } }, @@ -559,7 +569,7 @@ "preset_mode": { "state": { "power": "Shedding", - "security": "Safety", + "safety": "Safety", "none": "Manual", "frost": "Frost" } diff --git a/custom_components/versatile_thermostat/translations/fr.json b/custom_components/versatile_thermostat/translations/fr.json index 09f8df1..9b5fa85 100644 --- a/custom_components/versatile_thermostat/translations/fr.json +++ b/custom_components/versatile_thermostat/translations/fr.json @@ -5,6 +5,7 @@ "step": { "user": { "title": "Type du nouveau Versatile Thermostat", + "description": "Choisissez le type de thermostat que vous voulez créer [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/creation.md)", "data": { "thermostat_type": "Type de thermostat" }, @@ -35,7 +36,7 @@ }, "main": { "title": "Ajout d'un nouveau thermostat", - "description": "Principaux attributs obligatoires", + "description": "Principaux attributs obligatoires [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/base-attributes.md)", "data": { "name": "Nom", "thermostat_type": "Type de thermostat", @@ -71,7 +72,7 @@ }, "type": { "title": "Entité(s) liée(s)", - "description": "Attributs de(s) l'entité(s) liée(s)", + "description": "Attributs de(s) l'entité(s) liée(s) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/over-switch.md#configuration)", "data": { "underlying_entity_ids": "Les équipements à controller", "heater_keep_alive": "keep-alive (sec)", @@ -82,7 +83,11 @@ "auto_regulation_periode_min": "Période minimale de régulation", "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" + "auto_fan_mode": " Auto ventilation mode", + "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": { "underlying_entity_ids": "La liste des équipements qui seront controlés par ce VTherm", @@ -94,12 +99,13 @@ "auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation", "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" + "auto_fan_mode": "Active la ventilation automatiquement en cas d'écart important", + "on_command_text": "Pour les sous-jacents de type `select` ou `climate` vous devez personnaliser les commandes." } }, "tpi": { "title": "TPI", - "description": "Attributs de l'algo Time Proportional Integral", + "description": "Attributs de l'algo Time Proportional Integral [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coeff_int", "tpi_coef_ext": "coeff_ext", @@ -113,14 +119,14 @@ }, "presets": { "title": "Pre-réglages", - "description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques", + "description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-presets.md)", "data": { "use_presets_central_config": "Utiliser la configuration des pré-réglages centrale" } }, "window": { "title": "Gestion d'une ouverture", - "description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique.", + "description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-window.md)", "data": { "window_sensor_entity_id": "Détecteur d'ouverture (entity id)", "window_delay": "Délai de prise en compte à l'ouverture (secondes)", @@ -144,7 +150,7 @@ }, "motion": { "title": "Gestion de la détection de mouvement", - "description": "Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.", + "description": "Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-motion.md)", "data": { "motion_sensor_entity_id": "Détecteur de mouvement", "motion_delay": "Délai d'activation", @@ -164,7 +170,7 @@ }, "power": { "title": "Gestion de la puissance", - "description": "Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum.\nDonnez les entity id des capteurs qui mesurent la puissance totale et la puissance max autorisée.\nEnsuite donnez la puissance de l'équipement.\nTous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W).", + "description": "Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum.\nDonnez les entity id des capteurs qui mesurent la puissance totale et la puissance max autorisée.\nEnsuite donnez la puissance de l'équipement.\nTous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-power.md)", "data": { "power_sensor_entity_id": "Capteur de puissance totale (entity id)", "max_power_sensor_entity_id": "Capteur de puissance Max (entity id)", @@ -179,8 +185,8 @@ } }, "presence": { - "title": "Gestion de la présence", - "description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'abs.", + "title": "Gestion de la présenc", + "description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-presence.md)", "data": { "presence_sensor_entity_id": "Capteur de présence", "use_presence_central_config": "Utiliser la configuration centrale des températures en cas d'absence. Décochez pour avoir des entités de température dédiées" @@ -191,7 +197,7 @@ }, "advanced": { "title": "Parameters avancés", - "description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.", + "description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-advanced.md)", "data": { "minimal_activation_delay": "Délai minimal d'activation", "safety_delay_min": "Délai maximal entre 2 mesures de températures", @@ -209,7 +215,7 @@ }, "central_boiler": { "title": "Contrôle de la chaudière centrale", - "description": "Donnez les services à appeler pour allumer/éteindre la chaudière centrale. Laissez vide, si aucun appel de service ne doit être effectué (dans ce cas, vous devrez gérer vous même l'allumage/extinction de votre chaudière centrale). Le service a appelé doit être formatté comme suit: `entity_id/service_name[/attribut:valeur]` (/attribut:valeur est facultatif)\nPar exemple:\n- pour allumer un switch: `switch.controle_chaudiere/switch.turn_on`\n- pour éteindre un switch: `switch.controle_chaudiere/switch.turn_off`\n- pour programmer la chaudière sur 25° et ainsi forcer son allumage: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- pour envoyer 10° à la chaudière et ainsi forcer son extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Donnez les services à appeler pour allumer/éteindre la chaudière centrale. Laissez vide, si aucun appel de service ne doit être effectué (dans ce cas, vous devrez gérer vous même l'allumage/extinction de votre chaudière centrale). Le service a appelé doit être formatté comme suit: `entity_id/service_name[/attribut:valeur]` (/attribut:valeur est facultatif)\nPar exemple:\n- pour allumer un switch: `switch.controle_chaudiere/switch.turn_on`\n- pour éteindre un switch: `switch.controle_chaudiere/switch.turn_off`\n- pour programmer la chaudière sur 25° et ainsi forcer son allumage: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- pour envoyer 10° à la chaudière et ainsi forcer son extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Commande pour allumer", "central_boiler_deactivation_service": "Commande pour éteindre" @@ -221,7 +227,7 @@ }, "valve_regulation": { "title": "Auto-régulation par vanne", - "description": "Configuration de l'auto-régulation par controle direct de la vanne", + "description": "Configuration de l'auto-régulation par controle direct de la vanne [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Entités de 'calibrage du décalage''", "opening_degree_entity_ids": "Entités 'ouverture de vanne'", @@ -253,6 +259,7 @@ "step": { "user": { "title": "Type - {name}", + "description": "Choisissez le type de thermostat que vous voulez créer [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/creation.md)", "data": { "thermostat_type": "Type de thermostat" }, @@ -283,7 +290,7 @@ }, "main": { "title": "Attributs - {name}", - "description": "Principaux attributs obligatoires", + "description": "Principaux attributs obligatoires [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/base-attributes.md)", "data": { "name": "Nom", "thermostat_type": "Type de thermostat", @@ -319,7 +326,7 @@ }, "type": { "title": "Entité(s) liée(s) - {name}", - "description": "Attributs de(s) l'entité(s) liée(s)", + "description": "Attributs de(s) l'entité(s) liée(s) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/over-switch.md#configuration)", "data": { "underlying_entity_ids": "Les équipements à controller", "heater_keep_alive": "keep-alive (sec)", @@ -330,7 +337,11 @@ "auto_regulation_periode_min": "Période minimale de régulation", "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" + "auto_fan_mode": " Auto ventilation mode", + "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": { "underlying_entity_ids": "La liste des équipements qui seront controlés par ce VTherm", @@ -342,12 +353,13 @@ "auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation", "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" + "auto_fan_mode": "Active la ventilation automatiquement en cas d'écart important", + "on_command_text": "Pour les sous-jacents de type `select` ou `climate`" } }, "tpi": { "title": "TPI - {name}", - "description": "Attributs de l'algo Time Proportional Integral", + "description": "Attributs de l'algo Time Proportional Integral [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/algorithms.md#lalgorithme-tpi)", "data": { "tpi_coef_int": "coeff_int : Coefficient à utiliser pour le delta de température interne", "tpi_coef_ext": "coeff_ext : Coefficient à utiliser pour le delta de température externe" @@ -355,14 +367,14 @@ }, "presets": { "title": "Pre-réglages - {name}", - "description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques", + "description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-presets.md)", "data": { "use_presets_central_config": "Utiliser la configuration des pré-réglages centrale" } }, "window": { "title": "Ouverture - {name}", - "description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique.", + "description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-window.md)", "data": { "window_sensor_entity_id": "Détecteur d'ouverture (entity id)", "window_delay": "Délai avant extinction (secondes)", @@ -386,7 +398,7 @@ }, "motion": { "title": "Mouvement - {name}", - "description": "Gestion du mouvement. Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.", + "description": "Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-motion.md)", "data": { "motion_sensor_entity_id": "Détecteur de mouvement", "motion_delay": "Délai d'activation", @@ -406,7 +418,7 @@ }, "power": { "title": "Puissance - {name}", - "description": "Gestion de la puissance. Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum. Tous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W).", + "description": "Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum.\nDonnez les entity id des capteurs qui mesurent la puissance totale et la puissance max autorisée.\nEnsuite donnez la puissance de l'équipement.\nTous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W) [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-power.md)", "data": { "power_sensor_entity_id": "Puissance totale", "max_power_sensor_entity_id": "Capteur de puissance Max (entity id)", @@ -422,7 +434,7 @@ }, "presence": { "title": "Présence - {name}", - "description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'abs.", + "description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-presence.md)", "data": { "presence_sensor_entity_id": "Capteur de présence", "use_presence_central_config": "Utiliser la configuration centrale des températures en cas d'absence. Décochez pour avoir des entités de température dédiées" @@ -433,7 +445,7 @@ }, "advanced": { "title": "Avancés - {name}", - "description": "Paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.", + "description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-advanced.md)", "data": { "minimal_activation_delay": "Délai minimal d'activation", "safety_delay_min": "Délai maximal entre 2 mesures de températures", @@ -451,7 +463,7 @@ }, "central_boiler": { "title": "Contrôle de la chaudière centrale - {name}", - "description": "Donnez les services à appeler pour allumer/éteindre la chaudière centrale. Laissez vide, si aucun appel de service ne doit être effectué (dans ce cas, vous devrez gérer vous même l'allumage/extinction de votre chaudière centrale). Le service a appelé doit être formatté comme suit: `entity_id/service_name[/attribut:valeur]` (/attribut:valeur est facultatif)\nPar exemple:\n- pour allumer un switch: `switch.controle_chaudiere/switch.turn_on`\n- pour éteindre un switch: `switch.controle_chaudiere/switch.turn_off`\n- pour programmer la chaudière sur 25° et ainsi forcer son allumage: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- pour envoyer 10° à la chaudière et ainsi forcer son extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10`", + "description": "Donnez les services à appeler pour allumer/éteindre la chaudière centrale. Laissez vide, si aucun appel de service ne doit être effectué (dans ce cas, vous devrez gérer vous même l'allumage/extinction de votre chaudière centrale). Le service a appelé doit être formatté comme suit: `entity_id/service_name[/attribut:valeur]` (/attribut:valeur est facultatif)\nPar exemple:\n- pour allumer un switch: `switch.controle_chaudiere/switch.turn_on`\n- pour éteindre un switch: `switch.controle_chaudiere/switch.turn_off`\n- pour programmer la chaudière sur 25° et ainsi forcer son allumage: `climate.thermostat_chaudiere/climate.set_temperature/temperature:25`\n- pour envoyer 10° à la chaudière et ainsi forcer son extinction: `climate.thermostat_chaudiere/climate.set_temperature/temperature:10` [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/feature-central-boiler.md)", "data": { "central_boiler_activation_service": "Commande pour allumer", "central_boiler_deactivation_service": "Commande pour éteindre" @@ -463,7 +475,7 @@ }, "valve_regulation": { "title": "Auto-régulation par vanne - {name}", - "description": "Configuration de l'auto-régulation par controle direct de la vanne", + "description": "Configuration de l'auto-régulation par controle direct de la vanne [![?](https://img.icons8.com/color/18/help.png)](https://github.com/jmcollin78/versatile_thermostat/blob/main/documentation/fr/self-regulation.md#auto-régulation-par-contrôle-direct-de-la-vanne)", "data": { "offset_calibration_entity_ids": "Entités de 'calibrage du décalage''", "opening_degree_entity_ids": "Entités 'ouverture de vanne'", @@ -487,7 +499,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é" @@ -554,7 +567,7 @@ "preset_mode": { "state": { "power": "Délestage", - "security": "Sécurité", + "safety": "Sécurité", "none": "Manuel", "frost": "Hors Gel" } diff --git a/custom_components/versatile_thermostat/underlyings.py b/custom_components/versatile_thermostat/underlyings.py index 9f1e559..fc88f25 100644 --- a/custom_components/versatile_thermostat/underlyings.py +++ b/custom_components/versatile_thermostat/underlyings.py @@ -2,10 +2,12 @@ """ Underlying entities classes """ import logging -from typing import Any +import re +from typing import Any, Dict, Tuple + from enum import StrEnum -from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_UNAVAILABLE +from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_OFF, STATE_UNAVAILABLE from homeassistant.core import State from homeassistant.exceptions import ServiceNotFound @@ -209,17 +211,8 @@ class UnderlyingEntity: class UnderlyingSwitch(UnderlyingEntity): """Represent a underlying switch""" - _initialDelaySec: int - _on_time_sec: int - _off_time_sec: int - def __init__( - self, - hass: HomeAssistant, - thermostat: Any, - switch_entity_id: str, - initial_delay_sec: int, - keep_alive_sec: float, + self, hass: HomeAssistant, thermostat: Any, switch_entity_id: str, initial_delay_sec: int, keep_alive_sec: float, vswitch_on: str = None, vswitch_off: str = None ) -> None: """Initialize the underlying switch""" @@ -235,6 +228,14 @@ class UnderlyingSwitch(UnderlyingEntity): self._on_time_sec = 0 self._off_time_sec = 0 self._keep_alive = IntervalCaller(hass, keep_alive_sec) + self._vswitch_on = vswitch_on.strip() if vswitch_on else None + self._vswitch_off = vswitch_off.strip() if vswitch_off else None + self._domain = self._entity_id.split(".")[0] + # build command + command, data, state_on = self.build_command(use_on=True) + self._on_command = {"command": command, "data": data, "state": state_on} + command, data, state_off = self.build_command(use_on=False) + self._off_command = {"command": command, "data": data, "state": state_off} @property def initial_delay_sec(self): @@ -243,7 +244,7 @@ class UnderlyingSwitch(UnderlyingEntity): @overrides @property - def is_inversed(self): + def is_inversed(self) -> bool: """Tells if the switch command should be inversed""" return self._thermostat.is_inversed @@ -275,10 +276,15 @@ class UnderlyingSwitch(UnderlyingEntity): @property def is_device_active(self): """If the toggleable device is currently active.""" - real_state = self._hass.states.is_state(self._entity_id, STATE_ON) - return (self.is_inversed and not real_state) or ( - not self.is_inversed and real_state - ) + # real_state = self._hass.states.is_state(self._entity_id, STATE_ON) + # return (self.is_inversed and not real_state) or ( + # not self.is_inversed and real_state + # ) + is_on = self._hass.states.is_state(self._entity_id, self._on_command.get("state")) + if self.is_inversed: + return not is_on + + return is_on async def _keep_alive_callback(self): """Keep alive: Turn on if already turned on, turn off if already turned off.""" @@ -305,18 +311,48 @@ class UnderlyingSwitch(UnderlyingEntity): ) await (self.turn_on() if self.is_device_active else self.turn_off()) - # @overrides this breaks some unit tests TypeError: object MagicMock can't be used in 'await' expression + def build_command(self, use_on: bool) -> Tuple[str, Dict[str, str]]: + """Build a command and returns a command and a dict as data""" + + value = None + data = {ATTR_ENTITY_ID: self._entity_id} + take_on = (use_on and not self.is_inversed) or (not use_on and self.is_inversed) + vswitch = self._vswitch_on if take_on else self._vswitch_off + if vswitch: + pattern = r"^(?P[^\s/]+)(?:/(?P[^\s:]+)(?::(?P[^\s]+))?)?$" + match = re.match(pattern, vswitch) + + if match: + # Extraire les groupes nommés + command = match.group("command") + argument = match.group("argument") + value = match.group("value") + if argument is not None and value is not None: + data.update({argument: value}) + else: + raise ValueError(f"Invalid input format: {vswitch}. Must be conform to 'command[/argument[:value]]'") + + else: + command = SERVICE_TURN_ON if take_on else SERVICE_TURN_OFF + + if value is None: + value = STATE_ON if take_on else STATE_OFF + + return command, data, value + async def turn_off(self): """Turn heater toggleable device off.""" self._keep_alive.cancel() # Cancel early to avoid a turn_on/turn_off race condition _LOGGER.debug("%s - Stopping underlying entity %s", self, self._entity_id) - command = SERVICE_TURN_OFF if not self.is_inversed else SERVICE_TURN_ON - domain = self._entity_id.split(".")[0] + + command = self._off_command.get("command") + data = self._off_command.get("data") + # This may fails if called after shutdown try: try: - data = {ATTR_ENTITY_ID: self._entity_id} - await self._hass.services.async_call(domain, command, data) + _LOGGER.debug("%s - Sending command %s with data=%s", self, command, data) + await self._hass.services.async_call(self._domain, command, data) self._keep_alive.set_async_action(self._keep_alive_callback) except Exception: self._keep_alive.cancel() @@ -332,12 +368,12 @@ class UnderlyingSwitch(UnderlyingEntity): if not await self.check_overpowering(): return False - command = SERVICE_TURN_ON if not self.is_inversed else SERVICE_TURN_OFF - domain = self._entity_id.split(".")[0] + command = self._on_command.get("command") + data = self._on_command.get("data") try: try: - data = {ATTR_ENTITY_ID: self._entity_id} - await self._hass.services.async_call(domain, command, data) + _LOGGER.debug("%s - Sending command %s with data=%s", self, command, data) + await self._hass.services.async_call(self._domain, command, data) self._keep_alive.set_async_action(self._keep_alive_callback) return True except Exception: diff --git a/documentation/en/images/config-vswitch1.png b/documentation/en/images/config-vswitch1.png new file mode 100644 index 0000000000000000000000000000000000000000..31a50d6dc4ea56df0bb38b151484e8272a2bfd45 GIT binary patch literal 12358 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 h9eqG``v3oMJO1_$ns1{5a6G6Nq?3b-J The on-time percentage is recalculated each cycle, which is what allows regulating the room temperature. @@ -31,8 +32,11 @@ Then, click on the "Underlying Entities" option from the menu, and you will see ![image](images/config-linked-entity.png) -### The Underlying Entities -In the "Equipment to Control" list, you should add the switches that will be controlled by VTherm. Only `switch` or `input_boolean` entities are accepted. +### The underlying devices + +In the "list of devices to control," you add the switches that will be controlled by VTherm. Only entities of type `switch`, `input_boolean`, `select`, `input_select`, or `climate` are accepted. + +If one of the underlying devices is not a `switch`, then command customization is mandatory. By default, for `switch` entities, the commands are the standard switch on/off commands (`turn_on`, `turn_off`). The algorithm currently available is TPI. See [algorithm](#algorithm). If multiple entities are configured, the thermostat staggers the activations to minimize the number of switches on at any given time. This allows for better power distribution, as each radiator will turn on in turn. @@ -53,4 +57,39 @@ It is possible to choose a `thermostat_over_switch` to control an air conditione ### Command Inversion -If your equipment is controlled by a pilot wire with a diode, you may need to check the "Invert the Command" box. This will set the switch to `On` when you need to turn off the equipment and to `Off` when you need to turn it on. The cycle times will be inverted with this option. \ No newline at end of file +If your equipment is controlled by a pilot wire with a diode, you may need to check the "Invert the Command" box. This will set the switch to `On` when you need to turn off the equipment and to `Off` when you need to turn it on. The cycle times will be inverted with this option. + +### Command Customization + +This configuration section allows you to customize the on and off commands sent to the underlying device. +These commands are mandatory if one of the underlying devices is not a `switch` (for `switch` entities, standard on/off commands are used). + +To customize the commands, click on `Add` at the bottom of the page for both the on and off commands: + +![virtual switch](images/config-vswitch1.png) + +Then, specify the on and off commands using the format `command[/attribute[:value]]`. +The available commands depend on the type of underlying device: + +| Underlying Device Type | Possible On Commands | Possible Off Commands | Applies To | +| --------------------------- | ------------------------------------- | ----------------------------------- | ----------------------------- | +| `switch` or `input_boolean` | `turn_on` | `turn_off` | All switches | +| `select` or `input_select` | `select_option/option:comfort` | `set_option/option:frost` | Nodon SIN-4-FP-21 and similar | +| `climate` (hvac_mode) | `set_hvac_mode/hvac_mode:heat` | `set_hvac_mode/hvac_mode:off` | eCosy (via Tuya Local) | +| `climate` (preset) | `set_preset_mode/preset_mode:comfort` | `set_preset_mode/preset_mode:frost` | Heatzy | + +Of course, these examples can be adapted to your specific case. + +Example for a Nodon SIN-4-FP-21: +![virtual switch Nodon](images/config-vswitch2.png) + +Click "Validate" to confirm the modifications. + +If the following error occurs: + +> The command customization configuration is incorrect. It is required for non-switch underlying devices, and the format must be 'service_name[/attribute:value]'. More details in the README. + +This means that one of the entered commands is invalid. The following rules must be followed: +1. Each command must follow the format `command[/attribute[:value]]` (e.g., `select_option/option:comfort` or `turn_on`) without spaces or special characters except `_`. +2. There must be as many commands as there are declared underlying devices, except when all underlying devices are `switch` entities, in which case command customization is not required. +3. If multiple underlying devices are configured, the commands must be in the same order. The number of on commands must equal the number of off commands and the number of underlying devices (in the correct order). It is possible to mix different types of underlying devices. As soon as one underlying device is not a `switch`, all commands for all underlying devices, including `switch` entities, must be configured. diff --git a/documentation/en/quick-start.md b/documentation/en/quick-start.md new file mode 100644 index 0000000..230d6f1 --- /dev/null +++ b/documentation/en/quick-start.md @@ -0,0 +1,84 @@ +# Quick Start + +This page outlines the steps to quickly set up a basic yet operational _VTherm_. It is structured by equipment type. + +- [Quick Start](#quick-start) + - [Nodon SIN-4-FP-21 or similar (pilot wire)](#nodon-sin-4-fp-21-or-similar-pilot-wire) + - [Heatzy, eCosy, or similar (`climate` entity)](#heatzy-ecosy-or-similar-climate-entity) + - [Simple switch such as Aqara T1, Nous B2Z, Sonoff ZBMini, Sonoff POW, ...](#simple-switch-such-as-aqara-t1-nous-b2z-sonoff-zbmini-sonoff-pow-) + - [Sonoff TRVZB or similar (TRV with valve control)](#sonoff-trvzb-or-similar-trv-with-valve-control) +- [Next Steps](#next-steps) +- [Call for Contributions](#call-for-contributions) + +## Nodon SIN-4-FP-21 or similar (pilot wire) + +This module allows controlling a radiator via a pilot wire. It appears in _HA_ as a `select` entity that lets you choose the heating preset to apply. + +_VTherm_ will regulate the temperature by periodically changing the preset via customized commands until the setpoint is reached. + +For this to work, the preset used for heating control must be higher than the maximum temperature you will need (24°C is a good value). + +To integrate it into _VTherm_, you must: +1. Create a _VTherm_ of type `over_switch`. See [creating a _VTherm_](creation.md), +2. Assign it the main attributes (name, room temperature sensor, and outdoor temperature sensor at a minimum). See [main attributes](base-attributes.md), +3. Assign one or more underlying devices to control. The underlying device here is the `select` entity that controls the Nodon. See [underlying devices](over-switch.md), +4. Provide custom on/off commands (mandatory for the Nodon). See [command customization](over-switch.md#command-customization). The custom commands follow the format `select_option/option:` as indicated in the link. + +After completing these four steps, you will have a fully functional _VTherm_ that controls your Nodon or similar device. + +## Heatzy, eCosy, or similar (`climate` entity) + +This module allows controlling a radiator that appears in _HA_ as a `climate` entity, enabling you to choose the heating preset or mode (Heat / Cool / Off). + +_VTherm_ will regulate the temperature by turning the device on/off via customized commands at regular intervals until the setpoint is reached. + +To integrate it into _VTherm_, you must: +1. Create a _VTherm_ of type `over_switch`. See [creating a _VTherm_](creation.md), +2. Assign it the main attributes (name, room temperature sensor, and outdoor temperature sensor at a minimum). See [main attributes](base-attributes.md), +3. Assign one or more underlying devices to control. The underlying device here is the `climate` entity that controls the Heatzy or eCosy. See [underlying devices](over-switch.md), +4. Provide custom on/off commands (mandatory). See [command customization](over-switch.md#command-customization). The custom commands follow the format `set_hvac_mode/hvac_mode:` or `set_preset_mode/preset_mode:` as indicated in the link. + +After completing these four steps, you will have a fully functional _VTherm_ that controls your Heatzy, eCosy, or similar device. + +## Simple switch such as Aqara T1, Nous B2Z, Sonoff ZBMini, Sonoff POW, ... + +This module allows controlling a radiator via a simple switch. It appears in _HA_ as a `switch` entity that directly turns the radiator on or off. + +_VTherm_ will regulate the temperature by periodically turning the `switch` on and off until the setpoint is reached. + +To integrate it into _VTherm_, you must: +1. Create a _VTherm_ of type `over_switch`. See [creating a _VTherm_](creation.md), +2. Assign it the main attributes (name, room temperature sensor, and outdoor temperature sensor at a minimum). See [main attributes](base-attributes.md), +3. Assign one or more underlying devices to control. The underlying device here is the `switch` entity that controls the switch. See [underlying devices](over-switch.md). + +After completing these three steps, you will have a fully functional _VTherm_ that controls your `switch` or similar device. + +## Sonoff TRVZB or similar (TRV with valve control) + +This type of _TRV_ device controls the opening of a valve that allows more or less hot water from a boiler or heat pump to flow. It appears in _HA_ as a `climate` entity along with `number` entities that control the valve. These `number` entities may be hidden and need to be explicitly added in some cases. + +_VTherm_ will adjust the valve opening degree until the setpoint temperature is reached. + +To integrate it into _VTherm_, you must: +1. Create a _VTherm_ of type `over_climate`. See [creating a _VTherm_](creation.md), +2. Assign it the main attributes (name, room temperature sensor, and outdoor temperature sensor at a minimum). See [main attributes](base-attributes.md), +3. Assign one or more underlying devices to control. The underlying device here is the `climate` entity that controls the TRV. See [underlying devices](over-climate.md), +4. Specify the regulation type as `Direct valve control` only. Leave the option `Compensate for underlying temperature` unchecked. See [auto-regulation](over-climate.md#auto-regulation), +5. Provide the `number` entities named `opening_degree` and `calibration_offset`. Do not configure the `closing_degree` entity. See [underlying devices](over-switch.md). + +For this to work, the `closing degree` must be set to the maximum (100%). Do not immediately enable the `Follow underlying temperature change` option until you have verified that this basic configuration is working properly. + +After completing these five steps, you will have a fully functional _VTherm_ that controls your Sonoff TRVZB or similar device. + +# Next Steps + +Once created, you need to configure the preset temperatures. See [presets](feature-presets.md) for a minimal configuration. +You can also (optional but recommended) install the dedicated UI card for your dashboards. (See [VTHerm UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card)) + +Once this minimal setup is functional—and only once it works correctly—you can add additional features such as presence detection to avoid heating when no one is present. Add them one by one, verifying that _VTherm_ reacts correctly at each step before proceeding to the next. + +You can then set up centralized configurations to share settings across all _VTherm_ instances, enable central mode for unified control of all _VTherms_ ([centralized configuration](feature-central-mode.md)), or integrate a central boiler control ([central boiler](feature-central-boiler.md)). This is not an exhaustive list—please refer to the table of contents for a complete list of _VTherm_ features. + +# Call for Contributions + +This page is open for contributions. Feel free to suggest additional equipment and minimal configuration setups. \ No newline at end of file diff --git a/documentation/en/troubleshooting.md b/documentation/en/troubleshooting.md index 85b75f4..203f146 100644 --- a/documentation/en/troubleshooting.md +++ b/documentation/en/troubleshooting.md @@ -22,6 +22,10 @@ ## Using a Heatzy +The Heatzy is now natively supported by _VTherm_. See [Quick Start](quick-start.md#heatzy-or-ecosy-or-similar-climate-entity). + +This configuration is kept for reference only. + Using a Heatzy or Nodon is possible provided you use a virtual switch with this model: ```yaml @@ -53,6 +57,12 @@ Using a Heatzy or Nodon is possible provided you use a virtual switch with this Thanks to @gael for this example. ## Using a radiator with a pilot wire (Nodon SIN-4-FP-21) + +The Nodon is now natively supported by _VTherm_. See [Quick Start](quick-start.md#nodon-sin-4-fp-21-or-similar-pilot-wire). + +This configuration is kept for reference only. + + As with the Heatzy above, you can use a virtual switch that will change the preset of your radiator based on the VTherm’s on/off state. Example: diff --git a/documentation/fr/images/config-vswitch1.png b/documentation/fr/images/config-vswitch1.png new file mode 100644 index 0000000000000000000000000000000000000000..04fd8e3e1d5c439ed5fab98808aa4953786f0279 GIT binary patch literal 34098 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 G*#82~@8ih; literal 0 HcmV?d00001 diff --git a/documentation/fr/images/config-vswitch2.png b/documentation/fr/images/config-vswitch2.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef7cd48879c36ef62f897c74813f1a431419f51 GIT binary patch literal 56418 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℘6BU30Xd&@3>ka2e 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 Le pourcentage d'allumage est recalculé à chaque cycle et c'est ce qui permet de réguler la température de la pièce. @@ -31,7 +32,9 @@ Ensuite cliquez sur l'option de menu "Sous-jacents" et vous allez avoir cette pa ![image](images/config-linked-entity.png) ### les sous-jacents -Dans la "liste des équipements à contrôler" vous mettez les switchs qui vont être controllés par le VTherm. Seuls les entités de type `switch` ou `input_boolean` sont acceptées. +Dans la "liste des équipements à contrôler" vous mettez les switchs qui vont être controllés par le VTherm. Seuls les entités de type `switch` ou `input_boolean` ou `select` ou `input_select` ou `climate` sont acceptées. + +Si un des sous-jacents n'est pas un `switch` alors la personnalisation des commandes est obligatoires. Par défaut pour les `switch` les commandes sont les commandes classique d'allumage / extinction du switch (`turn_on`, `turn_off`) L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme). Si plusieurs entités de type sont configurées, la thermostat décale les activations afin de minimiser le nombre de switch actif à un instant t. Ca permet une meilleure répartition de la puissance puisque chaque radiateur va s'allumer à son tour. @@ -54,3 +57,37 @@ Il est possible de choisir un thermostat over switch qui commande une climatisat Si votre équipement est commandé par un fil pilote avec un diode, vous aurez certainement besoin de cocher la case "Inverser la case". Elle permet de mettre le switch à `On` lorsqu'on doit étiendre l'équipement et à `Off` lorsqu'on doit l'allumer. Les temps de cycle sont donc inversés avec cette option. +### La personnalisation des commandes + +Cette section de configuration permet de personnaliser les commandes d'allumage et d'extinction envoyée à l'équipement sous-jacent, +Ces commandes sont obligatoires si un des sous-jacents n'est pas un `switch` (pour les `switchs` les commandes d'allumage/extinction classiques sont utilisées). + +Pour personnaliser les commande, cliquez sur `Ajouter` en bas de page sur les commandes d'allumage et sur les commandes d'extinction : + +![virtual switch](images/config-vswitch1.png) + +et donner la commande d'allumage et d'exinction avec le format `commande[/attribut[:valeur]]`. +Les commandes possibles dépendent du type de sous-jacents : + +| type de sous-jacent | commandes d'allumage possibles | commandes d'extinction possibles | S'applique à | +| --------------------------- | ------------------------------------- | ----------------------------------- | ------------------------------ | +| `switch` ou `input_boolean` | `turn_on` | `turn_off` | tous les switchs | +| `select` ou `input_select` | `select_option/option:comfort` | `set_option/option:frost` | Nodon SIN-4-FP-21 et assimilés | +| `climate` (hvac_mode) | `set_hvac_mode/hvac_mode:heat` | `set_hvac_mode/hvac_mode:off` | eCosy (via Tuya Local) | +| `climate` (preset) | `set_preset_mode/preset_mode:comfort` | `set_preset_mode/preset_mode:frost` | Heatzy | + +Evidemment, tous ces exemples peuvent être adaptés à votre cas. + +Exemple pour un Nodon SIN-4-FP-21 : +![virtual switch Nodon](images/config-vswitch2.png) + +Cliquez sur valider pour accepter les modifications. + +Si l'erreur suivante se produit : + +> 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. + +Cela signifie que une des commandes saisies est invalide. Les règles à respecter sont les suivantes : +1. chaque commande doit avoir le format `commande[/attribut[:valeur]]` (ex: `select_option/option:comfort` ou `turn_on`) sans blanc et sans caractères spéciaux sauf '_', +2. il doit y avoir autant de commandes qu'il y a de sous-jacents déclarés sauf si tous les sous-jacents sont des `switchs` auquel cas il n'est pas nécessaire de paramétrer les commandes, +3. si plusieurs sous-jacents sont configurés, les commandes doivent être dans le même ordre. Le nombre de commandes d'allumage doit être égal au nombre de commandes d'extinction et de sous-jacents (dans l'ordre donc). Il est possible de mettre des sous-jacents de type différent. À partir du moment où un sous-jacent n'est pas un `switch`, il faut paramétrer toutes les commandes de tous les sous-jacents y compris des éventuels switchs. \ No newline at end of file diff --git a/documentation/fr/quick-start.md b/documentation/fr/quick-start.md new file mode 100644 index 0000000..cba2589 --- /dev/null +++ b/documentation/fr/quick-start.md @@ -0,0 +1,86 @@ +# Démarrage rapide + +Cette page présente les étapes à suivre pour avoir un _VTherm_ basique mais opérationnel rapidement. Elle est présentée par type d'équipements. + +- [Démarrage rapide](#démarrage-rapide) + - [Nodon SIN-4-FP-21 ou assimilé (fil pilote)](#nodon-sin-4-fp-21-ou-assimilé-fil-pilote) + - [Heatzy ou eCosy ou assimilé (entité `climate`)](#heatzy-ou-ecosy-ou-assimilé-entité-climate) + - [Simple switch comme Aqara T1, Nous B2Z, Sonoff ZBMini, Sonoff POW, ...](#simple-switch-comme-aqara-t1-nous-b2z-sonoff-zbmini-sonoff-pow-) + - [Sonoff TRVZB ou assimilé (TRV avec contrôle de la vanne)](#sonoff-trvzb-ou-assimilé-trv-avec-contrôle-de-la-vanne) +- [La suite](#la-suite) +- [Appel à contribution](#appel-à-contribution) + + +## Nodon SIN-4-FP-21 ou assimilé (fil pilote) + +Ce module permet de commander un radiateur avec un fil pilote. Il est visible sous _HA_ sous la forme d'une entité `select` qui permet de choisir le preset de chauffage à appliquer. + +_VTherm_ va réguler la température en changeant de preset via les commandes personnalisées régulièrement jusqu'à ce que la consigne soit atteinte. + +Pour que cela fonctionne, il faut le preset utiliser pour la commande de chauffage soit supérieur à la température maximale que vous aurez besoin (24° est une bonne valeur). + +Pour l'intégrer dans _VTherm_ vous devez : +1. Créer un _VTherm_ de type `over_switch`. Cf. [création d'un _VTherm_](creation.md), +2. Lui donner des principaux attributs (nom, capteur de température de la pièce et capteur de température extérieure au minimium). Cf. [principaux attributs](base-attributes.md), +3. Lui donner un ou plusieurs équipements sous-jacents à contrôler. Le sous-jacent est ici l'entité `select` qui contrôle le Nodon. Cf. [sous-jacent](over-switch.md), +4. Lui donner des commandes d'allumage et extinction personnalisées (obligatoire pour le Nodon). Cf. [sous-jacent](over-switch.md#la-personnalisation-des-commandes). Les commandes personnalisées sont du type `select_option/option:` comme indiqué dans le lien. + +A l'issue de ces 4 étapes vous avez un _VTherm_ opérationnel ultra simple qui contrôle votre Nodon ou assimilé. + +## Heatzy ou eCosy ou assimilé (entité `climate`) + +Ce module permet de commander un radiateur qui est visible sous _HA_ sous la forme d'une entité `climate` qui permet de choisir le preset de chauffage à appliquer ou le mode (Chauffe / Froid / éteint). + +_VTherm_ va réguler la température en allumant / éteignant via les commandes personnalisées régulièrement l'équipement jusqu'à ce que la consigne soit atteinte. + +Pour l'intégrer dans _VTherm_ vous devez : +1. Créer un _VTherm_ de type `over_switch`. Cf. [création d'un _VTherm_](creation.md), +2. Lui donner des principaux attributs (nom, capteur de température de la pièce et capteur de température extérieure au minimium). Cf. [principaux attributs](base-attributes.md), +3. Lui donner un ou plusieurs équipements sous-jacents à contrôler. Le sous-jacent est ici l'entité `climate` qui contrôle le Heatzy ou le eCosy. Cf. [sous-jacent](over-switch.md), +4. Lui donner des commandes d'allumage et extinction personnalisées (obligatoire). Cf. [sous-jacent](over-switch.md#la-personnalisation-des-commandes). Les commandes personnalisées sont du type `set_hvac_mode/hvac_mode:` ou `set_preset_mode/preset_mode:` comme indiqué dans le lien. + +A l'issue de ces 4 étapes vous avez un _VTherm_ opérationnel ultra simple qui commande votre équipement Heatzy ou eCosy ou assimilé. + +## Simple switch comme Aqara T1, Nous B2Z, Sonoff ZBMini, Sonoff POW, ... + +Ce module permet de commander un radiateur contrôlé par un simple switch. Il est visible sous _HA_ sous la forme d'une entité `switch` qui permet d'allumer ou éteindre directement le radiateur. + +_VTherm_ va réguler la température en allumant / éteignant régulièrement le `switch` jusqu'à ce que la consigne soit atteinte. + +Pour l'intégrer dans _VTherm_ vous devez : +1. Créer un _VTherm_ de type `over_switch`. Cf. [création d'un _VTherm_](creation.md), +2. Lui donner des principaux attributs (nom, capteur de température de la pièce et capteur de température extérieure au minimium). Cf. [principaux attributs](base-attributes.md), +3. Lui donner un ou plusieurs équipements sous-jacents à contrôler. Le sous-jacent est ici l'entité `switch` qui contrôle le switch. Cf. [sous-jacent](over-switch.md) + +A l'issue de ces 3 étapes vous avez un _VTherm_ opérationnel ultra simple qui contrôle votre `switch` ou assimilé. + +## Sonoff TRVZB ou assimilé (TRV avec contrôle de la vanne) + +Cet équipement de type _TRV_ contrôle l'ouverture d'une vanne qui laisse plus ou moins passer de l'eau chaude produit par une chaudière ou une PAC. Il est visible sous _HA_ sous la forme d'une entité de type `climate` et d'entités de type `number` qui vont permettre le contrôle de la vanne. Ces entités `number` peuvent être cachées et doivent être explicitement ajoutées dans certains cas. + +Le _VTherm_ va moduler le taux d'ouverture de la vanne jusqu'à obtention de la température de consigne. + +Pour l'intégrer dans _VTherm_ vous devez : +1. Créer un _VTherm_ de type `over_climate`. Cf. [création d'un _VTherm_](creation.md), +2. Lui donner des principaux attributs (nom, capteur de température de la pièce et capteur de température extérieure au minimium). Cf. [principaux attributs](base-attributes.md), +3. Lui donner un ou plusieurs équipements sous-jacents à contrôler. Le sous-jacent est ici l'entité `climate` qui contrôle le TRV. Cf. [sous-jacent](over-climate.md), +4. Spécifiez la régulation de type `Par contrôle direct de la vanne` uniquement. Laissez décochée l'option `Compenser la température interne du sous-jacent`. Cf. [sous-jacent](over-climate.md#lauto-régulation), +5. Lui donner les entités de type `number` nommées `opening_degree` et `calibration offset`. Ne pas renseigner l'entité `closing degree` Cf. [sous-jacent](over-switch.md), + +Pour que cela fonctionne, il faut que le `closing degree` soit réglé au maximum (100%). Ne cocher pas tout de suite l'entité `Follow underlying temperature change` avant d'avoir bien vérifier que cette configuration de base fonctionne. + +A l'issue de ces 5 étapes vous avez un _VTherm_ opérationnel ultra simple qui commande votre équipement Sonoff TRVZB ou assimilé. + +# La suite + +Uen fois créé, vous devez paramétrer les températures des presets. Cf. [presets](feature-presets.md) pour avoir une configuration minimale. +Vous pouvez aussi (facultatif mais conseillé) installer la carte dédiée pour vos dashboard. (Cf. [VTHerm UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card)) + +Une fois cette configuration minimale opérationnelle - et seulement lorsque cette configuration minimale fonctionne -, vous pourrez ajouter des fonctions supplémentaires comme la détection de présence pour éviter de chauffer lorsqu'il n'y a personne. Ajoutez les une par une, vérifiez à chaque nouvelle fonction que _VTherm_ réagit correctement avant de passer à la suivante. + +Ensuite vous pourrez ajouter une configuration centrale pour mettre en commum des réglages entre tous les _VTherm_, paraméter le mode central permettant de contrôler tous les _VTherms_ d'un coup ([la configuration centralisée](feature-central-mode.md)), ajouter un éventuel contrôle d'une chaudière centrale ([la chaudière centrale](feature-central-boiler.md)). Cette liste est non exhaustive, merci de consulter le sommaire pour avoir la liste de toutes les fonctions de _VTherm_. + +# Appel à contribution + +Cette page ne demande qu'à se compléter. N'hésitez pas à proposer vos équipements et la configuration minimale. + diff --git a/documentation/fr/self-regulation.md b/documentation/fr/self-regulation.md index 08c3e14..939efd3 100644 --- a/documentation/fr/self-regulation.md +++ b/documentation/fr/self-regulation.md @@ -2,8 +2,11 @@ # L'auto-régulation - [L'auto-régulation](#lauto-régulation) + - [Configuration](#configuration) + - [auto-régulation par contrôle direct de la vanne](#auto-régulation-par-contrôle-direct-de-la-vanne) + - [autres auto-régulation](#autres-auto-régulation) - [L'auto-régulation en mode Expert](#lauto-régulation-en-mode-expert) - - [Synthèse de l'algorithme d'auto-régulation](#synthèse-de-lalgorithme-dauto-régulation) + - [Synthèse de l'algorithme d'auto-régulation](#synthèse-de-lalgorithme-dauto-régulation) Vous avez la possibilité d'activer la fonction d'auto-régulation pour les _VTherm_ de type `over_climate` uniquement. diff --git a/documentation/fr/troubleshooting.md b/documentation/fr/troubleshooting.md index 9c5676a..36bd7a1 100644 --- a/documentation/fr/troubleshooting.md +++ b/documentation/fr/troubleshooting.md @@ -17,10 +17,15 @@ - [Utilisation d'un groupe de personnes comme capteur de présence](#utilisation-dun-groupe-de-personnes-comme-capteur-de-présence) - [Activer les logs du Versatile Thermostat](#activer-les-logs-du-versatile-thermostat) - [VTherm ne suit pas les changements de consigne faits directement depuis le sous-jacents (`over_climate`)](#vtherm-ne-suit-pas-les-changements-de-consigne-faits-directement-depuis-le-sous-jacents-over_climate) + - [VTherm passe tout seul en mode 'clim' ou en mode 'chauffage'](#vtherm-passe-tout-seul-en-mode-clim-ou-en-mode-chauffage) ## Utilisation d'un Heatzy +Le Heatzy est maintenant pris en charge nativement par _VTherm_. Cf. [Démarrage rapide](quick-start.md#heatzy-ou-ecosy-ou-assimilé-entité-climate). + +Cette configuration est gardée pour mémoire uniquement. + L'utilisation d'un Heatzy ou Nodon est possible à la condition d'utiliser un switch virtuel sur ce modèle : ```yaml @@ -52,6 +57,11 @@ L'utilisation d'un Heatzy ou Nodon est possible à la condition d'utiliser un sw Merci à @gael pour cet exemple. ## Utilisation d'un radiateur avec un fil pilote (Nodon SIN-4-FP-21) + +Le Nodon est maintenant pris en charge nativement par _VTherm_. Cf. [Démarrage rapide](quick-start.md#nodon-sin-4-fp-21-ou-assimilé-fil-pilote). + +Cette configuration est gardée pour mémoire uniquement. + Comme pour le Heatzy ci-dessus vous pouvez utiliser un switch virtuel qui va changer le preset de votre radiateur en fonction de l'état d'allumage du VTherm. Exemple : diff --git a/images/new-icon.png b/images/new-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fdabf8c52569aa2b95bd4f051609ef72e521df95 GIT binary patch literal 6822 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