Compare commits

..

7 Commits

Author SHA1 Message Date
Jean-Marc Collin fa248a3cfd Feature 218 add hors gel (#263)
* Implementation ok

* Add attributes for UI Card actions

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2023-12-10 16:56:01 +01:00
Jean-Marc Collin a7480e15c4 FIX anto_fan rule for Cooling mode. 2023-12-10 09:04:09 +00:00
Jean-Marc Collin 76416d28d2 Add instructions into ISSUE_TEMPLATE 2023-12-10 08:34:47 +00:00
Jean-Marc Collin 2bbd7ed8d6 Troubleshoot security mode 2023-12-10 00:10:34 +00:00
Jean-Marc Collin 7851df84ec Feature 223 use fan control in over climate (#260)
* Issue #223 - add auto_fan_mode

* Update README

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2023-12-09 18:39:54 +01:00
Jean-Marc Collin f7c4e20de3 Issue #257 - Migrate to HA 2023.12 2023-12-09 07:14:47 +00:00
Jean-Marc Collin 4d2888b220 Abyssal beers ! 2023-12-08 17:21:15 +00:00
33 changed files with 1586 additions and 487 deletions
+2 -1
View File
@@ -29,7 +29,8 @@
"donjayamanne.githistory",
"waderyan.gitblame",
"keesschollaart.vscode-home-assistant",
"vscode.markdown-math"
"vscode.markdown-math",
"yzhang.markdown-all-in-one"
],
// "mounts": [
// "source=${localWorkspaceFolder}/.devcontainer/configuration.yaml,target=${localWorkspaceFolder}/config/www/community/,type=bind,consistency=cached",
@@ -4,6 +4,12 @@ about: Suggest an idea for this project
---
**Consider the alternative to create a free discusssion before making a feature request**
Discussions forum is [here](https://github.com/jmcollin78/versatile_thermostat/discussions).
You should check that a discussion relative to the same issue have not been already answered in the forum.
Please also check in the [closed issues](https://github.com/jmcollin78/versatile_thermostat/issues) for a similar case.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+7 -2
View File
@@ -8,6 +8,8 @@ about: Create a report to help us improve
If you have a simple question or you are not sure this is an issue, don't open an issue but open a new discussion [here](https://github.com/jmcollin78/versatile_thermostat/discussions).
Check also in the [Troubleshooting](#troubleshooting) paragrah of the README if the aswer is not already given.
Issues not containing the minimum requirements will be closed:
- Issues without a description (using the header is not good enough) will be closed.
@@ -17,12 +19,12 @@ Issues not containing the minimum requirements will be closed:
## Version of the custom_component
<!-- If you are not using the newest version, download and try that before opening an issue
If you are unsure about the version check the const.py file.
If you are unsure about the version check the manifest.json file.
-->
## Configuration
<!-- Copy / paste the attributes of the VTherm here. You can go to Development Tool / States, find and select your VTherm and the copy/paste the attributes.
<!-- Copy / paste the attributes of the VTherm here. You can go to Development Tool / States, find and select your VTherm and the copy/paste the attributes. Surround these attributes by a yaml formatting ```yaml <put the attributes> .... ```
Without these attribute support is impossible due to the number of configuration attributes the VTherm have (more than 60). -->
My VTherm attributes are the following:
@@ -103,6 +105,9 @@ supported_features: 17
<!-- Please do not send an image but a copy / paste of the attributes in yaml format. -->
## If it is releveant to regulation performance or optimisation some curves are needed
To have a great curves demonstrating what you think is a problem, please install and configure what is described here: [Even better with Plotly to tune your Thermostat](#even-better-with-plotly-to-tune-your-thermostat)
## Describe the bug
A clear and concise description of what the bug is.
+1
View File
@@ -109,3 +109,4 @@ __pycache__
config/**
custom_components/hacs
custom_components/localtuya
+235 -152
View File
@@ -23,6 +23,7 @@
- [Pour un thermostat de type ```thermostat_over_climate```:](#pour-un-thermostat-de-type-thermostat_over_climate)
- [L'auto-régulation](#lauto-régulation)
- [L'auto-régulation en mode Expert](#lauto-régulation-en-mode-expert)
- [Le mode auto-fan](#le-mode-auto-fan)
- [Pour un thermostat de type ```thermostat_over_valve```:](#pour-un-thermostat-de-type-thermostat_over_valve)
- [Configurez les coefficients de l'algorithme TPI](#configurez-les-coefficients-de-lalgorithme-tpi)
- [Configurer la température préréglée](#configurer-la-température-préréglée)
@@ -48,14 +49,14 @@
- [Modifier la température des préréglages](#modifier-la-température-des-préréglages)
- [Modifier les paramètres de sécurité](#modifier-les-paramètres-de-sécurité)
- [ByPass Window Check](#bypass-window-check)
- [Notifications](#notifications)
- [Evènements](#evènements)
- [Attributs personnalisés](#attributs-personnalisés)
- [Quelques résultats](#quelques-résultats)
- [Encore mieux](#encore-mieux)
- [Bien mieux avec le Versatile Thermostat UI Card](#bien-mieux-avec-le-versatile-thermostat-ui-card)
- [Encore mieux avec le composant Scheduler !](#encore-mieux-avec-le-composant-scheduler-)
- [Encore bien mieux avec la custom:simple-thermostat front integration](#encore-bien-mieux-avec-la-customsimple-thermostat-front-integration)
- [Toujours mieux avec Apex-chart pour régler votre thermostat](#toujours-mieux-avec-apex-chart-pour-régler-votre-thermostat)
- [Toujours mieux avec Plotly pour régler votre thermostat](#toujours-mieux-avec-plotly-pour-régler-votre-thermostat)
- [Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements](#et-toujours-de-mieux-en-mieux-avec-laappdaemon-notifier-pour-notifier-les-évènements)
- [Les contributions sont les bienvenues !](#les-contributions-sont-les-bienvenues)
- [Dépannages](#dépannages)
@@ -63,11 +64,16 @@
- [Utilisation d'un radiateur avec un fil pilote](#utilisation-dun-radiateur-avec-un-fil-pilote)
- [Seul le premier radiateur chauffe](#seul-le-premier-radiateur-chauffe)
- [Régler les paramètres de détection d'ouverture de fenêtre en mode auto](#régler-les-paramètres-de-détection-douverture-de-fenêtre-en-mode-auto)
- [Pourquoi mon Versatile Thermostat se met en Securite ?](#pourquoi-mon-versatile-thermostat-se-met-en-securite-)
- [Comment détecter le mode sécurité ?](#comment-détecter-le-mode-sécurité-)
- [Comment être averti lorsque cela se produit ?](#comment-être-averti-lorsque-cela-se-produit-)
- [Comment réparer ?](#comment-réparer-)
Ce composant personnalisé pour Home Assistant est une mise à niveau et est une réécriture complète du composant "Awesome thermostat" (voir [Github](https://github.com/dadge/awesome_thermostat)) avec l'ajout de fonctionnalités.
> ![Nouveau](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*Nouveautés*_
> * **Release 4.3** : Ajout d'un mode auto-fan pour le type `over_climate` permettant d'activer la ventilation si l'écart de température est important [#223](https://github.com/jmcollin78/versatile_thermostat/issues/223).
> * **Release 4.2** : Le calcul de la pente de la courbe de température se fait maintenant en °/heure et non plus en °/min [#242](https://github.com/jmcollin78/versatile_thermostat/issues/242). Correction de la détection automatique des ouvertures par l'ajout d'un lissage de la courbe de température .
> * **Release 4.1** : Ajout d'un mode de régulation **Expert** dans lequel l'utilisateur peut spécifier ses propres paramètres d'auto-régulation au lieu d'utiliser les pre-programmés [#194](https://github.com/jmcollin78/versatile_thermostat/issues/194).
> * **Release 4.0** : Ajout de la prise en charge de la **Versatile Thermostat UI Card**. Voir [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card). Ajout d'un mode de régulation **Slow** pour les appareils de chauffage à latence lente [#168](https://github.com/jmcollin78/versatile_thermostat/issues/168). Changement de la façon dont **la puissance est calculée** dans le cas de VTherm avec des équipements multi-sous-jacents [#146](https://github.com/jmcollin78/versatile_thermostat/issues/146). Ajout de la prise en charge de AC et Heat pour VTherm via un interrupteur également [#144](https://github.com/jmcollin78/versatile_thermostat/pull/144)
@@ -93,7 +99,7 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et est une
2. Le seuil de détection automatique des ouvertures doit être spécifié en °/heure et pas plus en °/min. Pour conserver les mêmes paramètres il faut multiplier la valeur configurée par 60.
# Merci pour la bière [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
Un grand merci à @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess pour les bières. Ca fait très plaisir et ça m'encourage à continuer !
Un grand merci à @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess, @abyssmal pour les bières. Ca fait très plaisir et ça m'encourage à continuer !
# Quand l'utiliser et ne pas l'utiliser
@@ -315,6 +321,15 @@ et bien sur, configurer le mode auto-régulation du VTherm en mode Expert. Tous
Pour que les modifications soient prises en compte, il faut soit **relancer totalement Home Assistant** soit juste l'intégration Versatile Thermostat (Outils de dev / Yaml / rechargement de la configuration / Versatile Thermostat).
#### Le mode auto-fan
Ce mode introduit en 4.3 permet de forcer l'usage de la ventilation si l'écart de température est important. En effet, en activant la ventilation, la répartition se fait plus rapidement ce qui permet de gagner du temps dans l'atteinte de la température cible.
Vous pouvez choisir quelle ventilation vous voulez activer entre les paramètres suivants : Faible, Moyenne, Forte, Turbo.
Il faut évidemment que votre équipement sous-jacent soit équipée d'une ventilation et quelle soit pilotable pour que cela fonctionne.
Si votre équipement ne comprend pas le mode Turbo, le mode Forte` sera utilisé en remplacement.
Une fois l'écart de température redevenu faible, la ventilation se mettra dans un mode "normal" qui dépend de votre équipement à savoir (dans l'ordre) : `Silence (mute)`, `Auto (auto)`, `Faible (low)`. La première valeur qui est possible pour votre équipement sera choisie.
### Pour un thermostat de type ```thermostat_over_valve```:
![image](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/config-linked-entity3.png?raw=true)
Vous pouvez choisir jusqu'à entité du domaine ```number``` ou ```ìnput_number``` qui vont commander les vannes.
@@ -485,69 +500,69 @@ Voir [exemple de réglages](#examples-tuning) pour avoir des exemples de réglag
## Synthèse des paramètres
| Paramètre | Libellé | "over switch" | "over climate" | over valve |
| - | - | - | - | - |
| ``name`` | Nom | X | X | X |
| ``thermostat_type`` | Type de thermostat | X | X | X |
| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (auto-regulation) | X |
| ``external_temperature_sensor_entity_id`` | Température de l'exterieur sensor entity id | X | X (auto-regulation) | X |
| ``cycle_min`` | Durée du cycle (minutes) | X | X | X |
| ``temp_min`` | Température minimale permise | X | X | X |
| ``temp_max`` | Température maximale permise | X | X | X |
| ``device_power`` | Puissance de l'équipement | X | X | X |
| ``use_window_feature`` | Avec détection des ouvertures | X | X | X |
| ``use_motion_feature`` | Avec détection de mouvement | X | X | X |
| ``use_power_feature`` | Avec gestion de la puissance | X | X | X |
| ``use_presence_feature`` | Avec détection de présence | X | X | X |
| ``heater_entity1_id`` | 1er radiateur | X | - | - |
| ``heater_entity2_id`` | 2ème radiateur | X | - | - |
| ``heater_entity3_id`` | 3ème radiateur | X | - | - |
| ``heater_entity4_id`` | 4ème radiateur | X | - | - |
| ``proportional_function`` | Algorithme | X | - | - |
| ``climate_entity1_id`` | Thermostat sous-jacent | - | X | - |
| ``climate_entity2_id`` | 2ème thermostat sous-jacent | - | X | - |
| ``climate_entity3_id`` | 3ème thermostat sous-jacent | - | X | - |
| ``climate_entity4_id`` | 4ème thermostat sous-jacent | - | X | - |
| ``valve_entity1_id`` | Vanne sous-jacente | - | - | X |
| ``valve_entity2_id`` | 2ème vanne sous-jacente | - | - | X |
| ``valve_entity3_id`` | 3ème vanne sous-jacente | - | - | X |
| ``valve_entity4_id`` | 4ème vanne sous-jacente | - | - | X |
| ``ac_mode`` | utilisation de l'air conditionné (AC) ? | X | X | X |
| ``tpi_coef_int`` | Coefficient à utiliser pour le delta de température interne | X | - | X |
| ``tpi_coef_ext`` | Coefficient à utiliser pour le delta de température externe | X | - | X |
| ``eco_temp`` | Température en preset Eco | X | X | X |
| ``comfort_temp`` | Température en preset Confort | X | X | X |
| ``boost_temp`` | Température en preset Boost | X | X | X |
| ``eco_ac_temp`` | Température en preset Eco en mode AC | X | X | X |
| ``comfort_ac_temp`` | Température en preset Confort en mode AC | X | X | X |
| ``boost_ac_temp`` | Température en preset Boost en mode AC | X | X | X |
| ``window_sensor_entity_id`` | Détecteur d'ouverture (entity id) | X | X | X |
| ``window_delay`` | Délai avant extinction (secondes) | X | X | X |
| ``window_auto_open_threshold`` | Seuil haut de chute de température pour la détection automatique (en °/min) | X | X | X |
| ``window_auto_close_threshold`` | Seuil bas de chute de température pour la fin de détection automatique (en °/min) | X | X | X |
| ``window_auto_max_duration`` | Durée maximum d'une extinction automatique (en min) | X | X | X |
| ``motion_sensor_entity_id`` | Détecteur de mouvement entity id | X | X | X |
| ``motion_delay`` | Délai avant prise en compte du mouvement (seconds) | X | X | X |
| ``motion_off_delay`` | Délai avant prise en compte de la fin de mouvement (seconds) | X | X | X |
| ``motion_preset`` | Preset à utiliser si mouvement détecté | X | X | X |
| ``no_motion_preset`` | Preset à utiliser si pas de mouvement détecté | X | X | X |
| ``power_sensor_entity_id`` | Capteur de puissance totale (entity id) | X | X | X |
| ``max_power_sensor_entity_id`` | Capteur de puissance Max (entity id) | X | X | X |
| ``power_temp`` | Température si délestaqe | X | X | X |
| ``presence_sensor_entity_id`` | Capteur de présence entity id (true si quelqu'un est présent) | X | X | X |
| ``eco_away_temp`` | Température en preset Eco en cas d'absence | X | X | X |
| ``comfort_away_temp`` | Température en preset Comfort en cas d'absence | X | X | X |
| ``boost_away_temp`` | Température en preset Boost en cas d'absence | X | X | X |
| ``eco_ac_away_temp`` | Température en preset Eco en cas d'absence en mode AC | X | X | X |
| ``comfort_ac_away_temp`` | Température en preset Comfort en cas d'absence en mode AC | X | X | X |
| ``boost_ac_away_temp`` | Température en preset Boost en cas d'absence en mode AC | X | X | X |
| ``minimal_activation_delay`` | Délai minimal d'activation | X | - | - |
| ``security_delay_min`` | Délai maximal entre 2 mesures de températures | X | - | X |
| ``security_min_on_percent`` | Pourcentage minimal de puissance pour passer en mode sécurité | X | - | X |
| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - |
| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - |
| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - |
| ``inverse_switch_command`` | Inverse la commande du switch (pour switch avec fil pilote) | X | - | - |
| Paramètre | Libellé | "over switch" | "over climate" | over valve |
| ----------------------------------------- | --------------------------------------------------------------------------------- | ------------- | ------------------- | ---------- |
| ``name`` | Nom | X | X | X |
| ``thermostat_type`` | Type de thermostat | X | X | X |
| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (auto-regulation) | X |
| ``external_temperature_sensor_entity_id`` | Température de l'exterieur sensor entity id | X | X (auto-regulation) | X |
| ``cycle_min`` | Durée du cycle (minutes) | X | X | X |
| ``temp_min`` | Température minimale permise | X | X | X |
| ``temp_max`` | Température maximale permise | X | X | X |
| ``device_power`` | Puissance de l'équipement | X | X | X |
| ``use_window_feature`` | Avec détection des ouvertures | X | X | X |
| ``use_motion_feature`` | Avec détection de mouvement | X | X | X |
| ``use_power_feature`` | Avec gestion de la puissance | X | X | X |
| ``use_presence_feature`` | Avec détection de présence | X | X | X |
| ``heater_entity1_id`` | 1er radiateur | X | - | - |
| ``heater_entity2_id`` | 2ème radiateur | X | - | - |
| ``heater_entity3_id`` | 3ème radiateur | X | - | - |
| ``heater_entity4_id`` | 4ème radiateur | X | - | - |
| ``proportional_function`` | Algorithme | X | - | - |
| ``climate_entity1_id`` | Thermostat sous-jacent | - | X | - |
| ``climate_entity2_id`` | 2ème thermostat sous-jacent | - | X | - |
| ``climate_entity3_id`` | 3ème thermostat sous-jacent | - | X | - |
| ``climate_entity4_id`` | 4ème thermostat sous-jacent | - | X | - |
| ``valve_entity1_id`` | Vanne sous-jacente | - | - | X |
| ``valve_entity2_id`` | 2ème vanne sous-jacente | - | - | X |
| ``valve_entity3_id`` | 3ème vanne sous-jacente | - | - | X |
| ``valve_entity4_id`` | 4ème vanne sous-jacente | - | - | X |
| ``ac_mode`` | utilisation de l'air conditionné (AC) ? | X | X | X |
| ``tpi_coef_int`` | Coefficient à utiliser pour le delta de température interne | X | - | X |
| ``tpi_coef_ext`` | Coefficient à utiliser pour le delta de température externe | X | - | X |
| ``eco_temp`` | Température en preset Eco | X | X | X |
| ``comfort_temp`` | Température en preset Confort | X | X | X |
| ``boost_temp`` | Température en preset Boost | X | X | X |
| ``eco_ac_temp`` | Température en preset Eco en mode AC | X | X | X |
| ``comfort_ac_temp`` | Température en preset Confort en mode AC | X | X | X |
| ``boost_ac_temp`` | Température en preset Boost en mode AC | X | X | X |
| ``window_sensor_entity_id`` | Détecteur d'ouverture (entity id) | X | X | X |
| ``window_delay`` | Délai avant extinction (secondes) | X | X | X |
| ``window_auto_open_threshold`` | Seuil haut de chute de température pour la détection automatique (en °/min) | X | X | X |
| ``window_auto_close_threshold`` | Seuil bas de chute de température pour la fin de détection automatique (en °/min) | X | X | X |
| ``window_auto_max_duration`` | Durée maximum d'une extinction automatique (en min) | X | X | X |
| ``motion_sensor_entity_id`` | Détecteur de mouvement entity id | X | X | X |
| ``motion_delay`` | Délai avant prise en compte du mouvement (seconds) | X | X | X |
| ``motion_off_delay`` | Délai avant prise en compte de la fin de mouvement (seconds) | X | X | X |
| ``motion_preset`` | Preset à utiliser si mouvement détecté | X | X | X |
| ``no_motion_preset`` | Preset à utiliser si pas de mouvement détecté | X | X | X |
| ``power_sensor_entity_id`` | Capteur de puissance totale (entity id) | X | X | X |
| ``max_power_sensor_entity_id`` | Capteur de puissance Max (entity id) | X | X | X |
| ``power_temp`` | Température si délestaqe | X | X | X |
| ``presence_sensor_entity_id`` | Capteur de présence entity id (true si quelqu'un est présent) | X | X | X |
| ``eco_away_temp`` | Température en preset Eco en cas d'absence | X | X | X |
| ``comfort_away_temp`` | Température en preset Comfort en cas d'absence | X | X | X |
| ``boost_away_temp`` | Température en preset Boost en cas d'absence | X | X | X |
| ``eco_ac_away_temp`` | Température en preset Eco en cas d'absence en mode AC | X | X | X |
| ``comfort_ac_away_temp`` | Température en preset Comfort en cas d'absence en mode AC | X | X | X |
| ``boost_ac_away_temp`` | Température en preset Boost en cas d'absence en mode AC | X | X | X |
| ``minimal_activation_delay`` | Délai minimal d'activation | X | - | - |
| ``security_delay_min`` | Délai maximal entre 2 mesures de températures | X | - | X |
| ``security_min_on_percent`` | Pourcentage minimal de puissance pour passer en mode sécurité | X | - | X |
| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - |
| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - |
| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - |
| ``inverse_switch_command`` | Inverse la commande du switch (pour switch avec fil pilote) | X | - | - |
# Exemples de réglage
@@ -732,7 +747,7 @@ target:
entity_id : climate.my_thermostat
```
# Notifications
# Evènements
Les évènements marquant du thermostat sont notifiés par l'intermédiaire du bus de message.
Les évènements notifiés sont les suivants:
@@ -758,46 +773,46 @@ Pour régler l'algorithme, vous avez accès à tout le contexte vu et calculé p
Les attributs personnalisés sont les suivants :
| Attribut | Signification |
| ----------| --------|
| ``hvac_modes`` | La liste des modes supportés par le thermostat |
| ``temp_min`` | La température minimale |
| ``temp_max`` | La température maximale |
| ``preset_modes`` | Les préréglages visibles pour ce thermostat. Les préréglages cachés ne sont pas affichés ici |
| ``temperature_actuelle`` | La température actuelle telle que rapportée par le capteur |
| ``temperature`` | La température cible |
| ``action_hvac`` | L'action en cours d'exécution par le réchauffeur. Peut être inactif, chauffage |
| ``preset_mode`` | Le préréglage actuellement sélectionné. Peut être l'un des 'preset_modes' ou un préréglage caché comme power |
| ``[eco/confort/boost]_temp`` | La température configurée pour le préréglage xxx |
| ``[eco/confort/boost]_away_temp`` | La température configurée pour le préréglage xxx lorsque la présence est désactivée ou not_home |
| ``temp_power`` | La température utilisée lors de la détection de la perte |
| ``on_percent`` | Le pourcentage sur calculé par l'algorithme TPI |
| ``on_time_sec`` | La période On en sec. Doit être ```on_percent * cycle_min``` |
| ``off_time_sec`` | La période d'arrêt en sec. Doit être ```(1 - on_percent) * cycle_min``` |
| ``cycle_min`` | Le cycle de calcul en minutes |
| ``function`` | L'algorithme utilisé pour le calcul du cycle |
| ``tpi_coef_int`` | Le ``coef_int`` de l'algorithme TPI |
| ``tpi_coef_ext`` | Le ``coef_ext`` de l'algorithme TPI |
| ``saved_preset_mode`` | Le dernier preset utilisé avant le basculement automatique du preset |
| ``saved_target_temp`` | La dernière température utilisée avant la commutation automatique |
| ``window_state`` | Le dernier état connu du capteur de fenêtre. Aucun si la fenêtre n'est pas configurée |
| ``window_bypass_state`` | True si le bypass de la détection d'ouverture et activé |
| ``motion_state`` | Le dernier état connu du capteur de mouvement. Aucun si le mouvement n'est pas configuré |
| ``overpowering_state`` | Le dernier état connu du capteur surpuissant. Aucun si la gestion de l'alimentation n'est pas configurée |
| ``presence_state`` | Le dernier état connu du capteur de présence. Aucun si la gestion de présence n'est pas configurée |
| ``security_delay_min`` | Le délai avant de régler le mode de sécurité lorsque le capteur de température est éteint |
| ``security_min_on_percent`` | Pourcentage de chauffe en dessous duquel le thermostat ne passera pas en sécurité |
| ``security_default_on_percent`` | Pourcentage de chauffe utilisé lorsque le thermostat est en sécurité |
| ``last_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température interne |
| ``last_ext_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température extérieure |
| ``security_state`` | L'état de sécurité. vrai ou faux |
| ``minimal_activation_delay_sec`` | Le délai d'activation minimal en secondes |
| ``last_update_datetime`` | La date et l'heure au format ISO8866 de cet état |
| ``friendly_name`` | Le nom du thermostat |
| ``supported_features`` | Une combinaison de toutes les fonctionnalités prises en charge par ce thermostat. Voir la documentation officielle sur l'intégration climatique pour plus d'informations |
| ``valve_open_percent`` | Le pourcentage d'ouverture de la vanne |
| ``regulated_target_temperature`` | La température de consigne calculée par l'auto-régulation |
| ``is_inversed`` | True si la commande est inversée (fil pilote avec diode) |
| Attribut | Signification |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ``hvac_modes`` | La liste des modes supportés par le thermostat |
| ``temp_min`` | La température minimale |
| ``temp_max`` | La température maximale |
| ``preset_modes`` | Les préréglages visibles pour ce thermostat. Les préréglages cachés ne sont pas affichés ici |
| ``temperature_actuelle`` | La température actuelle telle que rapportée par le capteur |
| ``temperature`` | La température cible |
| ``action_hvac`` | L'action en cours d'exécution par le réchauffeur. Peut être inactif, chauffage |
| ``preset_mode`` | Le préréglage actuellement sélectionné. Peut être l'un des 'preset_modes' ou un préréglage caché comme power |
| ``[eco/confort/boost]_temp`` | La température configurée pour le préréglage xxx |
| ``[eco/confort/boost]_away_temp`` | La température configurée pour le préréglage xxx lorsque la présence est désactivée ou not_home |
| ``temp_power`` | La température utilisée lors de la détection de la perte |
| ``on_percent`` | Le pourcentage sur calculé par l'algorithme TPI |
| ``on_time_sec`` | La période On en sec. Doit être ```on_percent * cycle_min``` |
| ``off_time_sec`` | La période d'arrêt en sec. Doit être ```(1 - on_percent) * cycle_min``` |
| ``cycle_min`` | Le cycle de calcul en minutes |
| ``function`` | L'algorithme utilisé pour le calcul du cycle |
| ``tpi_coef_int`` | Le ``coef_int`` de l'algorithme TPI |
| ``tpi_coef_ext`` | Le ``coef_ext`` de l'algorithme TPI |
| ``saved_preset_mode`` | Le dernier preset utilisé avant le basculement automatique du preset |
| ``saved_target_temp`` | La dernière température utilisée avant la commutation automatique |
| ``window_state`` | Le dernier état connu du capteur de fenêtre. Aucun si la fenêtre n'est pas configurée |
| ``window_bypass_state`` | True si le bypass de la détection d'ouverture et activé |
| ``motion_state`` | Le dernier état connu du capteur de mouvement. Aucun si le mouvement n'est pas configuré |
| ``overpowering_state`` | Le dernier état connu du capteur surpuissant. Aucun si la gestion de l'alimentation n'est pas configurée |
| ``presence_state`` | Le dernier état connu du capteur de présence. Aucun si la gestion de présence n'est pas configurée |
| ``security_delay_min`` | Le délai avant d'activer le mode de sécurité lorsque un des 2 capteurs de température n'envoie plus de mesures |
| ``security_min_on_percent`` | Pourcentage de chauffe en dessous duquel le thermostat ne passera pas en sécurité |
| ``security_default_on_percent`` | Pourcentage de chauffe utilisé lorsque le thermostat est en sécurité |
| ``last_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température interne |
| ``last_ext_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température extérieure |
| ``security_state`` | L'état de sécurité. vrai ou faux |
| ``minimal_activation_delay_sec`` | Le délai d'activation minimal en secondes |
| ``last_update_datetime`` | La date et l'heure au format ISO8866 de cet état |
| ``friendly_name`` | Le nom du thermostat |
| ``supported_features`` | Une combinaison de toutes les fonctionnalités prises en charge par ce thermostat. Voir la documentation officielle sur l'intégration climatique pour plus d'informations |
| ``valve_open_percent`` | Le pourcentage d'ouverture de la vanne |
| ``regulated_target_temperature`` | La température de consigne calculée par l'auto-régulation |
| ``is_inversed`` | True si la commande est inversée (fil pilote avec diode) |
# Quelques résultats
@@ -902,53 +917,73 @@ Vous pouvez personnaliser ce composant à l'aide du composant HACS card-mod pour
```
![image](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/custom-css-thermostat.png?raw=true)
## Toujours mieux avec Apex-chart pour régler votre thermostat
Vous pouvez obtenir une courbe comme celle présentée dans [some results](#some-results) avec une sorte de configuration de graphique Apex uniquement en utilisant les attributs personnalisés du thermostat décrits [ici](#custom-attributes) :
## Toujours mieux avec Plotly pour régler votre thermostat
Vous pouvez obtenir une courbe comme celle présentée dans [some results](#some-results) avec une sorte de configuration de graphique Plotly uniquement en utilisant les attributs personnalisés du thermostat décrits [ici](#custom-attributes) :
Remplacez les valeurs entre [[ ]] par les votres.
```
type: custom:apexcharts-card
header:
show: true
title: Tuning chauffage
show_states: true
colorize_states: true
update_interval: 60sec
graph_span: 4h
yaxis:
- id: left
show: true
decimals: 2
- id: right
decimals: 2
show: true
opposite: true
series:
- entity: climate.thermostat_mythermostat
attribute: temperature
type: line
name: Target temp
curve: smooth
yaxis_id: left
- entity: climate.thermostat_mythermostat
attribute: current_temperature
name: Current temp
curve: smooth
yaxis_id: left
- entity: climate.thermostat_mythermostat <--- for over_switch
attribute: on_percent
name: Power percent
curve: stepline
yaxis_id: right
- entity: climate.thermostat_mythermostat <--- for over_thermostast
attribute: regulated_target_temperature
name: Regulated temperature
curve: stepline
yaxis_id: left
- entity: climate.thermostat_mythermostat <--- for over_valve
attribute: valve_open_percent
name: Valve open percent
curve: stepline
yaxis_id: right
- type: custom:plotly-graph
entities:
- entity: '[[climate]]'
attribute: temperature
yaxis: y1
name: Consigne
- entity: '[[climate]]'
attribute: current_temperature
yaxis: y1
name: T°
- entity: '[[climate]]'
attribute: ema_temp
yaxis: y1
name: Ema
- entity: '[[climate]]'
attribute: regulated_target_temperature
yaxis: y1
name: Regulated T°
- entity: '[[slope]]'
name: Slope
fill: tozeroy
yaxis: y9
fillcolor: rgba(100, 100, 100, 0.3)
line:
color: rgba(100, 100, 100, 0.9)
hours_to_show: 4
refresh_interval: 10
height: 800
config:
scrollZoom: true
layout:
margin:
r: 50
legend:
x: 0
'y': 1.2
groupclick: togglegroup
title:
side: top right
yaxis:
visible: true
position: 0
yaxis9:
visible: true
fixedrange: false
range:
- -0.5
- 0.5
position: 1
xaxis:
rangeselector:
'y': 1.1
x: 0.7
buttons:
- count: 1
step: hour
- count: 12
step: hour
- count: 1
step: day
- count: 7
step: day
```
## Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements
@@ -1119,6 +1154,54 @@ versatile_thermostat:
Ces paramètres sont sensibles et assez difficiles à régler. Merci de ne les utiliser que si vous savez ce que vous faites et que vos mesures de température ne sont pas déjà lisses.
## Pourquoi mon Versatile Thermostat se met en Securite ?
Le mode sécurité n'est possible que sur les VTherm `over_switch` et `over_valve`. Il survient lorsqu'un des 2 thermomètres qui donne la température de la pièce ou la température extérieure n'a pas envoyé de valeur depuis plus de `security_delay_min` minutes et que le radiateur chauffait à au moins `security_min_on_percent`.
Comme l'algorithme est basé sur les mesures de température, si elles ne sont plus reçues par le VTherm, il y a un risque de surchauffe et d'incendie. Pour éviter ça, lorsque les conditions rappelées ci-dessus sont détectées, la chauffe est limité au paramètre `security_default_on_percent`. Cette valeur doit donc être raisonnablement faible (10% est une bonne valeur). Elle permet d'éviter un incendie tout en évitant de couper totalement le radiateur (risque de gel).
Tous ces paramètres se règlent dans la dernière page de la configuration du VTherm : "Paramètres avancés".
### Comment détecter le mode sécurité ?
Le premier symptôme est une température anormalement basse avec un temps de chauffe faible à chaque cycle et régulier.
Exemple:
[security mode](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/security-mode-symptome1.png?raw=true)
Si vous avez installé la carte [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card), le VTherm en question aura cette forme là :
[security mode UI Card](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/security-mode-symptome2.png?raw=true)
Vous pouvez aussi vérifier dans les attributs du VTherm les dates de réception des différentes dates. **Les attributs sont disponibles dans les Outils de développement / Etats**.
Exemple :
```
security_state: true
last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"
last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00"
last_update_datetime: "2023-12-06T18:43:28.351103+01:00"
...
security_delay_min: 60
```
On voit que :
1. le VTherm est bien en mode sécurité (`security_state: true`),
2. l'heure courante est le 06/12/2023 à 18h43:28 (`last_update_datetime: "2023-12-06T18:43:28.351103+01:00"`),
3. l'heure de dernière réception de la température intérieure est le 06/12/2023 à 18h43:28 (`last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"`). Elle est donc récente,
4. l'heure de dernière réception de la température extérieure est le 06/12/2023 à 13h04:35 (`last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00`). C'est donc l'heure extérieure qui a plus de 5 h de retard et qui a provoquée le passage en mode sécurité, car le seuil est limité à 60 min (`security_delay_min: 60`).
### Comment être averti lorsque cela se produit ?
Pour être averti, le VTherm envoie un évènement dès que ça se produit et un en fin d'alerte sécurité. Vous pouvez capter ces évènements dans une automatisation et envoyer une notification par exemple, faire clignoter un voyant, déclencher une sirène, ... A vous de voir.
Pour manipuler les évènements générés par le VTherm, cf. [Eveènements](#evènements).
### Comment réparer ?
Cela va dépendre de la cause du problème :
1. Si un capteur est en défaut, il faut le réparer (remettre des piles, le changer, vérifier l'intégration Météo qui donne la température extérieure, ...),
2. Si le paramètre `security_delay_min` est trop petit, cela rsique de générer beaucoup de fausses alertes. Une valeur correcte est de l'ordre de 60 min, surtout si vous avez des capteurs de température à pile.
3. Certains capteurs de température, n'envoie pas de mesure si la température n'a pas changée. Donc en cas de température très stable pendant longtemps, le mode sécurité peut se déclencher. Ce n'est pas très grave puisqu'il s'enlève dès que le VTherm reçoit à nouveau une température. Sur certain thermomètre (TuYA par exemple), on peut forcer le délai max entre 2 mesures. Il conviendra de mettre un délai max < `security_delay_min`,
4. Dès que la température sera a nouveau reçue le mode sécurité s'enlèvera et les valeurs précédentes de preset, température cible et mode seront restaurées.
***
[versatile_thermostat]: https://github.com/jmcollin78/versatile_thermostat
+236 -154
View File
@@ -23,6 +23,7 @@
- [For a thermostat of type ```thermostat_over_climate```:](#for-a-thermostat-of-type-thermostat_over_climate)
- [Self-regulation](#self-regulation)
- [Self-regulation in Expert mode](#self-regulation-in-expert-mode)
- [Auto-fan mode](#auto-fan-mode)
- [For a thermostat of type ```thermostat_over_valve```:](#for-a-thermostat-of-type-thermostat_over_valve)
- [Configure the TPI algorithm coefficients](#configure-the-tpi-algorithm-coefficients)
- [Configure the preset temperature](#configure-the-preset-temperature)
@@ -47,14 +48,14 @@
- [Change the temperature of presets](#change-the-temperature-of-presets)
- [Change security settings](#change-security-settings)
- [ByPass Window Check](#bypass-window-check)
- [Notifications](#notifications)
- [Events](#events)
- [Custom attributes](#custom-attributes)
- [Some results](#some-results)
- [Even better](#even-better)
- [Much better with the Veersatile Thermostat UI Card](#much-better-with-the-veersatile-thermostat-ui-card)
- [Even Better with Scheduler Component !](#even-better-with-scheduler-component-)
- [Even-even better with custom:simple-thermostat front integration](#even-even-better-with-customsimple-thermostat-front-integration)
- [Even better with Apex-chart to tune your Thermostat](#even-better-with-apex-chart-to-tune-your-thermostat)
- [Even better with Plotly to tune your Thermostat](#even-better-with-plotly-to-tune-your-thermostat)
- [And always better and better with the NOTIFIER daemon app to notify events](#and-always-better-and-better-with-the-notifier-daemon-app-to-notify-events)
- [Contributions are welcome!](#contributions-are-welcome)
- [Troubleshooting](#troubleshooting)
@@ -62,12 +63,17 @@
- [Using a Heatsink with a Pilot Wire](#using-a-heatsink-with-a-pilot-wire)
- [Only the first radiator heats](#only-the-first-radiator-heats)
- [Adjust window opening detection parameters in auto mode](#adjust-window-opening-detection-parameters-in-auto-mode)
- [Why does my Versatile Thermostat go into Safety?](#why-does-my-versatile-thermostat-go-into-safety)
- [How to detect security mode?](#how-to-detect-security-mode)
- [How can I be notified when this happens?](#how-can-i-be-notified-when-this-happens)
- [How to repair?](#how-to-repair)
This custom component for Home Assistant is an upgrade and is a complete rewrite of the component "Awesome thermostat" (see [Github](https://github.com/dadge/awesome_thermostat)) with addition of features.
>![New](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/new-icon.png?raw=true) _*News*_
> * **Release 4.2**: The calculation of the slope of the temperature curve is now done in °/hour and no longer in °/min [#242](https://github.com/jmcollin78/versatile_thermostat/ issues/242). Correction of automatic detection of openings by adding smoothing of the temperature curve.
> * **Release 4.3**: Added an auto-fan mode for the `over_climate` type allowing ventilation to be activated if the temperature difference is significant [#223](https://github.com/jmcollin78/versatile_thermostat/issues/223).
> * **Release 4.2**: The calculation of the slope of the temperature curve is now done in °/hour and no longer in °/min [#242](https://github.com/jmcollin78/versatile_thermostat/issues/242). Correction of automatic detection of openings by adding smoothing of the temperature curve.
> * **Release 4.1**: Added an **Expert** regulation mode in which the user can specify their own auto-regulation parameters instead of using the pre-programmed ones [#194]( https://github.com/jmcollin78/versatile_thermostat/issues/194).
> * **Release 4.0**: Added the support of **Versatile Thermostat UI Card**. See [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card). Added a **Slow** regulation mode for slow latency heating devices [#168](https://github.com/jmcollin78/versatile_thermostat/issues/168). Change the way **the power is calculated** in case of VTherm with multi-underlying equipements [#146](https://github.com/jmcollin78/versatile_thermostat/issues/146). Added the support of AC and Heat for VTherm over switch alse [#144](https://github.com/jmcollin78/versatile_thermostat/pull/144)
> * **Release 3.8**: Added a **self-regulation function** for `over climate` thermostats whose regulation is done by the underlying climate. See [Self-regulation](#self-regulation) and [#129](https://github.com/jmcollin78/versatile_thermostat/issues/129). Added the possibility of **inverting the command** for an `over switch` thermostat to address installations with pilot wire and diode [#124](https://github.com/jmcollin78/versatile_thermostat/issues/124).
@@ -92,7 +98,7 @@ This custom component for Home Assistant is an upgrade and is a complete rewrite
2. The threshold for auto window auto detection should be specified in °/hour and no more in °/min. To keep the same parameters you have to multiply the configured value by 60.
# Thanks for the beer [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
Many thanks to @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess for the beers. It's very nice and encourages me to continue!
Many thanks to @salabur, @pvince83, @bergoglio, @EPicLURcher, @ecolorado66, @Kriss1670, @maia, @f.maymil, @moutte69, @Jerome, @Gunnar M, @Greg.o, @John Burgess, @abyssmal for the beers. It's very nice and encourages me to continue!
# When to use / not use
This thermostat can control 3 types of equipment:
@@ -311,6 +317,14 @@ and of course, configure the VTherm's self-regulation mode in **Expert** mode. A
For the changes to be taken into account, you must either **completely restart Home Assistant** or just the **Versatile Thermostat integration** (Dev tools / Yaml / reloading the configuration / Versatile Thermostat).
#### Auto-fan mode
This mode introduced in 4.3 makes it possible to force the use of ventilation if the temperature difference is significant. In fact, by activating ventilation, distribution occurs more quickly, which saves time in reaching the target temperature.
You can choose which ventilation you want to activate between the following settings: Low, Medium, High, Turbo.
Obviously your underlying equipment must be equipped with ventilation and be controllable for this to work.
If your equipment does not include Turbo mode, Forte` mode will be used as a replacement.
Once the temperature difference becomes low again, the ventilation will go into a "normal" mode which depends on your equipment, namely (in order): `Silence (mute)`, `Auto (auto)`, `Low (low)`. The first value that is possible for your equipment will be chosen.
### For a thermostat of type ```thermostat_over_valve```:
![image](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/config-linked-entity3.png?raw=true)
You can choose up to domain entity ```number``` or ```ìnput_number``` which will control the valves.
@@ -469,70 +483,70 @@ See [example tuning](#examples-tuning) for common tuning examples
## Parameters synthesis
| Paramètre | Libellé | "over switch" | "over climate" | "over valve" |
| ----------| --------| --- | --- | -- |
| ``name`` | Name | X | X | X |
| ``thermostat_type`` | Thermostat type | X | X | X |
| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (self-regulation) | X |
| ``external_temperature_sensor_entity_id`` | External temperature sensor entity id | X | X (self-regulation) | X |
| ``cycle_min`` | Cycle duration (minutes) | X | X | X |
| ``temp_min`` | Minimal temperature allowed | X | X | X |
| ``temp_max`` | Maximal temperature allowed | X | X | X |
| ``device_power`` | Device power | X | X | X |
| ``use_window_feature`` | Use window detection | X | X | X |
| ``use_motion_feature`` | Use motion detection | X | X | X |
| ``use_power_feature`` | Use power management | X | X | X |
| ``use_presence_feature`` | Use presence detection | X | X | X |
| ``heater_entity1_id`` | 1rst heater switch | X | - | - |
| ``heater_entity2_id`` | 2nd heater switch | X | - | - |
| ``heater_entity3_id`` | 3rd heater switch | X | - | - |
| ``heater_entity4_id`` | 4th heater switch | X | - | - |
| ``proportional_function`` | Algorithm | X | - | X |
| ``climate_entity1_id`` | 1rst underlying climate | - | X | - |
| ``climate_entity2_id`` | 2nd underlying climate | - | X | - |
| ``climate_entity3_id`` | 3rd underlying climate | - | X | - |
| ``climate_entity4_id`` | 4th underlying climate | - | X | - |
| ``valve_entity1_id`` | 1rst underlying valve | - | - | X |
| ``valve_entity2_id`` | 2nd underlying valve | - | - | X |
| ``valve_entity3_id`` | 3rd underlying valve | - | - | X |
| ``valve_entity4_id`` | 4th underlying valve | - | - | X |
| ``ac_mode`` | Use the Air Conditioning (AC) mode | X | X | X |
| ``tpi_coef_int`` | Coefficient to use for internal temperature delta | X | - | X |
| ``tpi_coef_ext`` | Coefficient to use for external temperature delta | X | - | X |
| ``eco_temp`` | Temperature in Eco preset | X | X | X |
| ``comfort_temp`` | Temperature in Comfort preset | X | X | X |
| ``boost_temp`` | Temperature in Boost preset | X | X | X |
| ``eco_ac_temp`` | Temperature in Eco preset for AC mode | X | X | X |
| ``comfort_ac_temp`` | Temperature in Comfort preset for AC mode | X | X | X |
| ``boost_ac_temp`` | Temperature in Boost preset for AC mode | X | X | X |
| ``window_sensor_entity_id`` | Window sensor entity id | X | X | X |
| ``window_delay`` | Window sensor delay (seconds) | X | X | X |
| ``window_auto_open_threshold`` | Temperature decrease threshold for automatic window open detection (in °/min) | X | X | X |
| ``window_auto_close_threshold`` | Temperature increase threshold for end of automatic detection (in °/min) | X | X | X |
| ``window_auto_max_duration`` | Maximum duration of automatic window open detection (in min) | X | X | X |
| ``motion_sensor_entity_id`` | Motion sensor entity id | X | X | X |
| ``motion_delay`` | Delay before considering the motion (seconds) | X | X | X |
| ``motion_off_delay`` | Delay before considering the end of motion (seconds) | X | X | X |
| ``motion_preset`` | Preset to use when motion is detected | X | X | X |
| ``no_motion_preset`` | Preset to use when no motion is detected | X | X | X |
| ``power_sensor_entity_id`` | Power sensor entity id | X | X | X |
| ``max_power_sensor_entity_id`` | Max power sensor entity id | X | X | X |
| ``power_temp`` | Temperature for Power shedding | X | X | X |
| ``presence_sensor_entity_id`` | Presence sensor entity id | X | X | X |
| ``eco_away_temp`` | Temperature in Eco preset when no presence | X | X | X |
| ``comfort_away_temp`` | Temperature in Comfort preset when no presence | X | X | X |
| ``boost_away_temp`` | Temperature in Boost preset when no presence | X | X | X |
| ``eco_ac_away_temp`` | Temperature in Eco preset when no presence in AC mode | X | X | X |
| ``comfort_ac_away_temp`` | Temperature in Comfort preset when no presence in AC mode | X | X | X |
| ``boost_ac_away_temp`` | Temperature in Boost preset when no presence in AC mode | X | X | X |
| ``minimal_activation_delay`` | Minimal activation delay | X | - | X |
| ``security_delay_min`` | Security delay (in minutes) | X | - | X |
| ``security_min_on_percent`` | Minimal power percent to enable security mode | X | - | X |
| ``security_default_on_percent`` | Power percent to use in security mode | X | - | X |
| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - |
| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - |
| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - |
| ``inverse_switch_command`` | Inverse the switch command (for pilot wire switch) | X | - | - |
| Paramètre | Libellé | "over switch" | "over climate" | "over valve" |
| ----------------------------------------- | ----------------------------------------------------------------------------- | ------------- | ------------------- | ------------ |
| ``name`` | Name | X | X | X |
| ``thermostat_type`` | Thermostat type | X | X | X |
| ``temperature_sensor_entity_id`` | Temperature sensor entity id | X | X (self-regulation) | X |
| ``external_temperature_sensor_entity_id`` | External temperature sensor entity id | X | X (self-regulation) | X |
| ``cycle_min`` | Cycle duration (minutes) | X | X | X |
| ``temp_min`` | Minimal temperature allowed | X | X | X |
| ``temp_max`` | Maximal temperature allowed | X | X | X |
| ``device_power`` | Device power | X | X | X |
| ``use_window_feature`` | Use window detection | X | X | X |
| ``use_motion_feature`` | Use motion detection | X | X | X |
| ``use_power_feature`` | Use power management | X | X | X |
| ``use_presence_feature`` | Use presence detection | X | X | X |
| ``heater_entity1_id`` | 1rst heater switch | X | - | - |
| ``heater_entity2_id`` | 2nd heater switch | X | - | - |
| ``heater_entity3_id`` | 3rd heater switch | X | - | - |
| ``heater_entity4_id`` | 4th heater switch | X | - | - |
| ``proportional_function`` | Algorithm | X | - | X |
| ``climate_entity1_id`` | 1rst underlying climate | - | X | - |
| ``climate_entity2_id`` | 2nd underlying climate | - | X | - |
| ``climate_entity3_id`` | 3rd underlying climate | - | X | - |
| ``climate_entity4_id`` | 4th underlying climate | - | X | - |
| ``valve_entity1_id`` | 1rst underlying valve | - | - | X |
| ``valve_entity2_id`` | 2nd underlying valve | - | - | X |
| ``valve_entity3_id`` | 3rd underlying valve | - | - | X |
| ``valve_entity4_id`` | 4th underlying valve | - | - | X |
| ``ac_mode`` | Use the Air Conditioning (AC) mode | X | X | X |
| ``tpi_coef_int`` | Coefficient to use for internal temperature delta | X | - | X |
| ``tpi_coef_ext`` | Coefficient to use for external temperature delta | X | - | X |
| ``eco_temp`` | Temperature in Eco preset | X | X | X |
| ``comfort_temp`` | Temperature in Comfort preset | X | X | X |
| ``boost_temp`` | Temperature in Boost preset | X | X | X |
| ``eco_ac_temp`` | Temperature in Eco preset for AC mode | X | X | X |
| ``comfort_ac_temp`` | Temperature in Comfort preset for AC mode | X | X | X |
| ``boost_ac_temp`` | Temperature in Boost preset for AC mode | X | X | X |
| ``window_sensor_entity_id`` | Window sensor entity id | X | X | X |
| ``window_delay`` | Window sensor delay (seconds) | X | X | X |
| ``window_auto_open_threshold`` | Temperature decrease threshold for automatic window open detection (in °/min) | X | X | X |
| ``window_auto_close_threshold`` | Temperature increase threshold for end of automatic detection (in °/min) | X | X | X |
| ``window_auto_max_duration`` | Maximum duration of automatic window open detection (in min) | X | X | X |
| ``motion_sensor_entity_id`` | Motion sensor entity id | X | X | X |
| ``motion_delay`` | Delay before considering the motion (seconds) | X | X | X |
| ``motion_off_delay`` | Delay before considering the end of motion (seconds) | X | X | X |
| ``motion_preset`` | Preset to use when motion is detected | X | X | X |
| ``no_motion_preset`` | Preset to use when no motion is detected | X | X | X |
| ``power_sensor_entity_id`` | Power sensor entity id | X | X | X |
| ``max_power_sensor_entity_id`` | Max power sensor entity id | X | X | X |
| ``power_temp`` | Temperature for Power shedding | X | X | X |
| ``presence_sensor_entity_id`` | Presence sensor entity id | X | X | X |
| ``eco_away_temp`` | Temperature in Eco preset when no presence | X | X | X |
| ``comfort_away_temp`` | Temperature in Comfort preset when no presence | X | X | X |
| ``boost_away_temp`` | Temperature in Boost preset when no presence | X | X | X |
| ``eco_ac_away_temp`` | Temperature in Eco preset when no presence in AC mode | X | X | X |
| ``comfort_ac_away_temp`` | Temperature in Comfort preset when no presence in AC mode | X | X | X |
| ``boost_ac_away_temp`` | Temperature in Boost preset when no presence in AC mode | X | X | X |
| ``minimal_activation_delay`` | Minimal activation delay | X | - | X |
| ``security_delay_min`` | Security delay (in minutes) | X | - | X |
| ``security_min_on_percent`` | Minimal power percent to enable security mode | X | - | X |
| ``security_default_on_percent`` | Power percent to use in security mode | X | - | X |
| ``auto_regulation_mode`` | Le mode d'auto-régulation | - | X | - |
| ``auto_regulation_dtemp`` | La seuil d'auto-régulation | - | X | - |
| ``auto_regulation_period_min`` | La période minimale d'auto-régulation | - | X | - |
| ``inverse_switch_command`` | Inverse the switch command (for pilot wire switch) | X | - | - |
# Examples tuning
@@ -715,7 +729,7 @@ target:
entity_id : climate.my_thermostat
```
# Notifications
# Events
Significant thermostat events are notified via the message bus.
The notified events are as follows:
@@ -741,46 +755,46 @@ To tune the algorithm you have access to all context seen and calculted by the t
Custom attributes are the following:
| Attribute | Meaning |
| ----------| --------|
| ``hvac_modes`` | The list of modes supported by the thermostat |
| ``min_temp`` | The minimal temperature |
| ``max_temp`` | The maximal temperature |
| ``preset_modes`` | The presets visible for this thermostat. Hidden presets are not showed here |
| ``current_temperature`` | The current temperature as reported by the sensor |
| ``temperature`` | The target temperature |
| ``hvac_action`` | The action currently running by the heater. Can be idle, heating |
| ``preset_mode`` | The currently selected preset. Can be one of the 'preset_modes' or a hidden preset like power |
| ``[eco/comfort/boost]_temp`` | The temperature configured for the preset xxx |
| ``[eco/comfort/boost]_away_temp`` | The temperature configured for the preset xxx when presence is off or not_home |
| ``power_temp`` | The temperature used when shedding is detected |
| ``on_percent`` | The percentage on calculated by the TPI algorithm |
| ``on_time_sec`` | The On period in sec. Should be ```on_percent * cycle_min``` |
| ``off_time_sec`` | The Off period in sec. Should be ```(1 - on_percent) * cycle_min``` |
| ``cycle_min`` | The calculation cycle in minutes |
| ``function`` | The algorithm used for cycle calculation |
| ``tpi_coef_int`` | The ``coef_int`` of the TPI algorithm |
| ``tpi_coef_ext`` | The ``coef_ext`` of the TPI algorithm |
| ``saved_preset_mode`` | The last preset used before automatic switch of the preset |
| ``saved_target_temp`` | The last temperature used before automatic switching |
| ``window_state`` | The last known state of the window sensor. None if window is not configured |
| ``window_bypass_state`` | True if the bypass of the window detection is activated |
| ``motion_state`` | The last known state of the motion sensor. None if motion is not configured |
| ``overpowering_state`` | The last known state of the overpowering sensor. None if power management is not configured |
| ``presence_state`` | The last known state of the presence sensor. None if presence management is not configured |
| ``security_delay_min`` | The delay before setting the security mode when temperature sensor are off |
| ``security_min_on_percent`` | The minimal on_percent below which security preset won't be trigger |
| ``security_default_on_percent`` | The on_percent used when thermostat is in ``security`` |
| ``last_temperature_datetime`` | The date and time in ISO8866 format of the last internal temperature reception |
| ``last_ext_temperature_datetime`` | The date and time in ISO8866 format of the last external temperature reception |
| ``security_state`` | The security state. true or false |
| ``minimal_activation_delay_sec`` | The minimal activation delay in seconds |
| ``last_update_datetime`` | The date and time in ISO8866 format of this state |
| ``friendly_name`` | The name of the thermostat |
| ``supported_features`` | A combination of all features supported by this thermostat. See official climate integration documentation for more informations |
| ``valve_open_percent`` | The opening percentage of the valve |
| ``regulated_target_temperature`` | The self-regulated target temperature calculated |
| ``is_inversed`` | True if the command is inversed (pilot wire with diode) |
| Attribute | Meaning |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| ``hvac_modes`` | The list of modes supported by the thermostat |
| ``min_temp`` | The minimal temperature |
| ``max_temp`` | The maximal temperature |
| ``preset_modes`` | The presets visible for this thermostat. Hidden presets are not showed here |
| ``current_temperature`` | The current temperature as reported by the sensor |
| ``temperature`` | The target temperature |
| ``hvac_action`` | The action currently running by the heater. Can be idle, heating |
| ``preset_mode`` | The currently selected preset. Can be one of the 'preset_modes' or a hidden preset like power |
| ``[eco/comfort/boost]_temp`` | The temperature configured for the preset xxx |
| ``[eco/comfort/boost]_away_temp`` | The temperature configured for the preset xxx when presence is off or not_home |
| ``power_temp`` | The temperature used when shedding is detected |
| ``on_percent`` | The percentage on calculated by the TPI algorithm |
| ``on_time_sec`` | The On period in sec. Should be ```on_percent * cycle_min``` |
| ``off_time_sec`` | The Off period in sec. Should be ```(1 - on_percent) * cycle_min``` |
| ``cycle_min`` | The calculation cycle in minutes |
| ``function`` | The algorithm used for cycle calculation |
| ``tpi_coef_int`` | The ``coef_int`` of the TPI algorithm |
| ``tpi_coef_ext`` | The ``coef_ext`` of the TPI algorithm |
| ``saved_preset_mode`` | The last preset used before automatic switch of the preset |
| ``saved_target_temp`` | The last temperature used before automatic switching |
| ``window_state`` | The last known state of the window sensor. None if window is not configured |
| ``window_bypass_state`` | True if the bypass of the window detection is activated |
| ``motion_state`` | The last known state of the motion sensor. None if motion is not configured |
| ``overpowering_state`` | The last known state of the overpowering sensor. None if power management is not configured |
| ``presence_state`` | The last known state of the presence sensor. None if presence management is not configured |
| ``security_delay_min`` | The delay before setting the security mode when temperature sensor are off |
| ``security_min_on_percent`` | The minimal on_percent below which security preset won't be trigger |
| ``security_default_on_percent`` | The on_percent used when thermostat is in ``security`` |
| ``last_temperature_datetime`` | The date and time in ISO8866 format of the last internal temperature reception |
| ``last_ext_temperature_datetime`` | The date and time in ISO8866 format of the last external temperature reception |
| ``security_state`` | The security state. true or false |
| ``minimal_activation_delay_sec`` | The minimal activation delay in seconds |
| ``last_update_datetime`` | The date and time in ISO8866 format of this state |
| ``friendly_name`` | The name of the thermostat |
| ``supported_features`` | A combination of all features supported by this thermostat. See official climate integration documentation for more informations |
| ``valve_open_percent`` | The opening percentage of the valve |
| ``regulated_target_temperature`` | The self-regulated target temperature calculated |
| ``is_inversed`` | True if the command is inversed (pilot wire with diode) |
# Some results
@@ -884,53 +898,73 @@ You can customize this component using the HACS card-mod component to adjust the
```
![image](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/custom-css-thermostat.png?raw=true)
## Even better with Apex-chart to tune your Thermostat
You can get curve like presented in [some results](#some-results) with kind of Apex-chart configuration only using the custom attributes of the thermostat described [here](#custom-attributes):
## Even better with Plotly to tune your Thermostat
You can get curve like presented in [some results](#some-results) with kind of Plotly configuration only using the custom attributes of the thermostat described [here](#custom-attributes):
Replace values in [[ ]] by yours.
```
type: custom:apexcharts-card
header:
show: true
title: Tuning chauffage
show_states: true
colorize_states: true
update_interval: 60sec
graph_span: 4h
yaxis:
- id: left
show: true
decimals: 2
- id: right
decimals: 2
show: true
opposite: true
series:
- entity: climate.thermostat_mythermostat
attribute: temperature
type: line
name: Target temp
curve: smooth
yaxis_id: left
- entity: climate.thermostat_mythermostat
attribute: current_temperature
name: Current temp
curve: smooth
yaxis_id: left
- entity: climate.thermostat_mythermostat <--- for over_switch
attribute: on_percent
name: Power percent
curve: stepline
yaxis_id: right
- entity: climate.thermostat_mythermostat <--- for over_thermostast
attribute: regulated_target_temperature
name: Regulated temperature
curve: stepline
yaxis_id: left
- entity: climate.thermostat_mythermostat <--- for over_valve
attribute: valve_open_percent
name: Valve open percent
curve: stepline
yaxis_id: right
- type: custom:plotly-graph
entities:
- entity: '[[climate]]'
attribute: temperature
yaxis: y1
name: Consigne
- entity: '[[climate]]'
attribute: current_temperature
yaxis: y1
name: T°
- entity: '[[climate]]'
attribute: ema_temp
yaxis: y1
name: Ema
- entity: '[[climate]]'
attribute: regulated_target_temperature
yaxis: y1
name: Regulated T°
- entity: '[[slope]]'
name: Slope
fill: tozeroy
yaxis: y9
fillcolor: rgba(100, 100, 100, 0.3)
line:
color: rgba(100, 100, 100, 0.9)
hours_to_show: 4
refresh_interval: 10
height: 800
config:
scrollZoom: true
layout:
margin:
r: 50
legend:
x: 0
'y': 1.2
groupclick: togglegroup
title:
side: top right
yaxis:
visible: true
position: 0
yaxis9:
visible: true
fixedrange: false
range:
- -0.5
- 0.5
position: 1
xaxis:
rangeselector:
'y': 1.1
x: 0.7
buttons:
- count: 1
step: hour
- count: 12
step: hour
- count: 1
step: day
- count: 7
step: day
```
## And always better and better with the NOTIFIER daemon app to notify events
@@ -1100,6 +1134,54 @@ versatile_thermostat:
These parameters are sensitive and quite difficult to adjust. Please only use them if you know what you are doing and your temperature measurements are not already smooth.
## Why does my Versatile Thermostat go into Safety?
Safety mode is only possible on VTherm `over_switch` and `over_valve`. It occurs when one of the 2 thermometers which gives the room temperature or the outside temperature has not sent a value for more than `security_delay_min` minutes and the radiator was heating at least `security_min_on_percent`.
As the algorithm is based on temperature measurements, if they are no longer received by the VTherm, there is a risk of overheating and fire. To avoid this, when the conditions mentioned above are detected, heating is limited to the `security_default_on_percent` parameter. This value must therefore be reasonably low. It helps prevent a fire while avoiding completely cutting off the radiator (risk of freezing).
All these parameters are adjusted on the last page of the VTherm configuration: “Advanced parameters”.
### How to detect security mode?
The first symptom is an abnormally low temperature with a slow and regular heating time in each cycle.
Example:
[security mode](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/security-mode-symptome1.png?raw=true)
If you installed the [Versatile Thermostat UI Card](https://github.com/jmcollin78/versatile-thermostat-ui-card), the VTherm in question will have this shape:
[security mode UI Card](https://github.com/jmcollin78/versatile_thermostat/blob/main/images/security-mode-symptome2.png?raw=true)
You can also check in the VTherm attributes the dates of receipt of the different dates. Attributes are available in Development Tools / Reports.
Example :
```
security_state: true
last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"
last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00"
last_update_datetime: "2023-12-06T18:43:28.351103+01:00"
...
security_delay_min: 60
```
We see that :
1. the VTherm is in security mode (`security_state: true`),
2. the current time is 06/12/2023 at 18:43:28 (`last_update_datetime: "2023-12-06T18:43:28.351103+01:00"`),
3. the last received time of indoor temperature is 06/12/2023 at 18:43:28 (`last_temperature_datetime: "2023-12-06T18:43:28.346010+01:00"`). So she's on time,
4. the last reception time of the outdoor temperature is 06/12/2023 at 1:04:35 p.m. (`last_ext_temperature_datetime: "2023-12-06T13:04:35.164367+01:00`). external time which is more than 5 hours late and which caused the switch to security mode, because the threshold is limited to 60 min (`security_delay_min: 60`)
### How can I be notified when this happens?
To be notified, the VTherm sends an event as soon as it happens and one at the end of the security alert. You can capture these events in an automation and send a notification for example, flash a light, trigger a siren, etc. It's up to you.
To manipulate the events generated by VTherm, cf. [Events](#events).
### How to repair?
This will depend on the cause of the problem:
1. If a sensor is faulty, it must be repaired (replace batteries, change it, check the Weather integration which gives the outside temperature, etc.),
2. If the `security_delay_min` parameter is too small, it risks generating a lot of false alerts. A correct value is around 60 min, especially if you have battery-powered temperature sensors.
3. Some temperature sensors do not send a measurement if the temperature has not changed. So in the event of a very stable temperature for a long time, the safety mode may be triggered. This is not very serious since it is removed as soon as the VTherm receives a temperature again. On certain thermometers (TuYA for example), you can force the maximum delay between 2 measurements. It will be appropriate to set a max delay < `security_delay_min`,
4. As soon as the temperature is received again the security mode will be removed and the previous values of preset, target temperature and mode will be restored.
***
[versatile_thermostat]: https://github.com/jmcollin78/versatile_thermostat
@@ -88,6 +88,7 @@ from .const import (
CONF_PRESENCE_SENSOR,
CONF_PRESET_POWER,
SUPPORT_FLAGS,
PRESET_FROST_PROTECTION,
PRESET_POWER,
PRESET_SECURITY,
PROPORTIONAL_FUNCTION_TPI,
@@ -102,7 +103,6 @@ from .const import (
CONF_TEMP_MIN,
HIDDEN_PRESETS,
CONF_AC_MODE,
UnknownEntity,
EventType,
ATTR_MEAN_POWER_CYCLE,
ATTR_TOTAL_ENERGY,
@@ -149,9 +149,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
{
"is_on",
"type",
"frost_temp",
"eco_temp",
"boost_temp",
"comfort_temp",
"frost_away_temp",
"eco_away_temp",
"boost_away_temp",
"comfort_away_temp",
@@ -259,6 +261,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._ema_temp = None
self._ema_algo = None
self._now = None
self._attr_fan_mode = None
self.post_init(entry_infos)
def post_init(self, entry_infos):
@@ -271,6 +275,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
)
self._ac_mode = entry_infos.get(CONF_AC_MODE) is True
self._attr_max_temp = entry_infos.get(CONF_TEMP_MAX)
self._attr_min_temp = entry_infos.get(CONF_TEMP_MIN)
# convert entry_infos into usable attributes
presets = {}
items = CONF_PRESETS_WITH_AC.items() if self._ac_mode else CONF_PRESETS.items()
@@ -280,6 +287,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
presets[key] = entry_infos.get(value)
else:
_LOGGER.debug("value %s not found in Entry", value)
presets[key] = (
self._attr_max_temp if self._ac_mode else self._attr_min_temp
)
presets_away = {}
items = (
@@ -293,6 +303,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
presets_away[key] = entry_infos.get(value)
else:
_LOGGER.debug("value %s not found in Entry", value)
presets_away[key] = (
self._attr_max_temp if self._ac_mode else self._attr_min_temp
)
if self._window_call_cancel is not None:
self._window_call_cancel()
@@ -309,8 +322,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._proportional_function = entry_infos.get(CONF_PROP_FUNCTION)
self._temp_sensor_entity_id = entry_infos.get(CONF_TEMP_SENSOR)
self._ext_temp_sensor_entity_id = entry_infos.get(CONF_EXTERNAL_TEMP_SENSOR)
self._attr_max_temp = entry_infos.get(CONF_TEMP_MAX)
self._attr_min_temp = entry_infos.get(CONF_TEMP_MIN)
# Default value not configurable
self._attr_target_temperature_step = 0.1
self._power_sensor_entity_id = entry_infos.get(CONF_POWER_SENSOR)
@@ -555,11 +566,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.async_on_remove(self.remove_thermostat)
try:
await self.async_startup()
except UnknownEntity:
# Ingore this error which is possible if underlying climate is not found temporary
pass
await self.async_startup()
def remove_thermostat(self):
"""Called when the thermostat will be removed"""
@@ -577,12 +584,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
need_write_state = False
# Initialize all UnderlyingEntities
for under in self._underlyings:
try:
under.startup()
except UnknownEntity:
# Not found, we will try later
pass
self.init_underlyings()
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
if temperature_state and temperature_state.state not in (
@@ -723,6 +725,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
EVENT_HOMEASSISTANT_START, _async_startup_internal
)
def init_underlyings(self):
"""Initialize all underlyings. Should be overriden if necessary"""
def restore_specific_previous_state(self, old_state):
"""Should be overriden in each specific thermostat
if a specific previous state or attribute should be
@@ -1110,9 +1115,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
await under.set_hvac_mode(hvac_mode) or need_control_heating
)
# If AC is on maybe we have to change the temperature in force mode
# If AC is on maybe we have to change the temperature in force mode, but not in frost mode (there is no Frost protection possible in AC mode)
if self._ac_mode:
await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
if self.preset_mode != PRESET_FROST_PROTECTION:
await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
else:
await self._async_set_preset_mode_internal(PRESET_ECO, True)
if need_control_heating and sub_need_control_heating:
await self.async_control_heating(force=True)
@@ -2089,6 +2097,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
return shouldBeInSecurity
@property
def is_initialized(self) -> bool:
"""Check if all underlyings are initialized
This is usefull only for over_climate in which we
should have found the underlying climate to be operational"""
return True
async def async_control_heating(self, force=False, _=None):
"""The main function used to run the calculation at each cycle"""
@@ -2104,18 +2119,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
await self._async_manage_window_auto(in_cycle=True)
# Issue 56 in over_climate mode, if the underlying climate is not initialized, try to initialize it
for under in self._underlyings:
if not under.is_initialized:
_LOGGER.info(
"%s - Underlying %s is not initialized. Try to initialize it",
self,
under.entity_id,
)
try:
under.startup()
except UnknownEntity:
# still not found, we an stop here
return False
if not self.is_initialized:
if not self.init_underlyings():
# still not found, we an stop here
return False
# Check overpowering condition
# Not necessary for switch because each switch is checking at startup
@@ -2171,9 +2178,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"hvac_mode": self.hvac_mode,
"preset_mode": self.preset_mode,
"type": self._thermostat_type,
"frost_temp": self._presets[PRESET_FROST_PROTECTION],
"eco_temp": self._presets[PRESET_ECO],
"boost_temp": self._presets[PRESET_BOOST],
"comfort_temp": self._presets[PRESET_COMFORT],
"frost_away_temp": self._presets_away.get(
self.get_preset_away_name(PRESET_FROST_PROTECTION)
),
"eco_away_temp": self._presets_away.get(
self.get_preset_away_name(PRESET_ECO)
),
@@ -15,7 +15,13 @@ from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers import entity_platform
from homeassistant.const import CONF_NAME, STATE_ON, STATE_OFF, STATE_HOME, STATE_NOT_HOME
from homeassistant.const import (
CONF_NAME,
STATE_ON,
STATE_OFF,
STATE_HOME,
STATE_NOT_HOME,
)
from .const import (
DOMAIN,
@@ -26,10 +32,11 @@ from .const import (
SERVICE_SET_SECURITY,
SERVICE_SET_WINDOW_BYPASS,
SERVICE_SET_AUTO_REGULATION_MODE,
SERVICE_SET_AUTO_FAN_MODE,
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_SWITCH,
CONF_THERMOSTAT_CLIMATE,
CONF_THERMOSTAT_VALVE
CONF_THERMOSTAT_VALVE,
)
from .thermostat_switch import ThermostatOverSwitch
@@ -102,8 +109,7 @@ async def async_setup_entry(
platform.async_register_entity_service(
SERVICE_SET_WINDOW_BYPASS,
{
vol.Required("window_bypass"): vol.In([True, False]
),
vol.Required("window_bypass"): vol.In([True, False]),
},
"service_set_window_bypass_state",
)
@@ -111,7 +117,19 @@ async def async_setup_entry(
platform.async_register_entity_service(
SERVICE_SET_AUTO_REGULATION_MODE,
{
vol.Required("auto_regulation_mode"): vol.In(["None", "Light", "Medium", "Strong", "Slow"]),
vol.Required("auto_regulation_mode"): vol.In(
["None", "Light", "Medium", "Strong", "Slow"]
),
},
"service_set_auto_regulation_mode",
)
platform.async_register_entity_service(
SERVICE_SET_AUTO_FAN_MODE,
{
vol.Required("auto_fan_mode"): vol.In(
["None", "Low", "Medium", "High", "Turbo"]
),
},
"service_set_auto_fan_mode",
)
@@ -107,6 +107,9 @@ from .const import (
CONF_INVERSE_SWITCH,
UnknownEntity,
WindowOpenDetectionMethod,
CONF_AUTO_FAN_MODES,
CONF_AUTO_FAN_MODE,
CONF_AUTO_FAN_HIGH,
)
_LOGGER = logging.getLogger(__name__)
@@ -275,6 +278,14 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
vol.Optional(
CONF_AUTO_REGULATION_PERIOD_MIN, default=5
): cv.positive_int,
vol.Optional(
CONF_AUTO_FAN_MODE, default=CONF_AUTO_FAN_HIGH
): selector.SelectSelector(
selector.SelectSelectorConfig(
options=CONF_AUTO_FAN_MODES,
translation_key="auto_fan_mode",
)
),
}
)
@@ -320,7 +331,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
self.STEP_PRESETS_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
{
vol.Optional(v, default=0.0): vol.Coerce(float)
vol.Optional(v, default=0): vol.Coerce(float)
for (k, v) in CONF_PRESETS.items()
}
)
@@ -328,7 +339,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
self.STEP_PRESETS_WITH_AC_DATA_SCHEMA = ( # pylint: disable=invalid-name
vol.Schema( # pylint: disable=invalid-name
{
vol.Optional(v, default=0.0): vol.Coerce(float)
vol.Optional(v, default=0): vol.Coerce(float)
for (k, v) in CONF_PRESETS_WITH_AC.items()
}
)
@@ -29,6 +29,7 @@ DEVICE_MODEL = "Versatile Thermostat"
PRESET_POWER = "power"
PRESET_SECURITY = "security"
PRESET_FROST_PROTECTION = "frost"
HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
@@ -95,6 +96,12 @@ CONF_AUTO_REGULATION_DTEMP = "auto_regulation_dtemp"
CONF_AUTO_REGULATION_PERIOD_MIN = "auto_regulation_periode_min"
CONF_INVERSE_SWITCH = "inverse_switch_command"
CONF_SHORT_EMA_PARAMS = "short_ema_params"
CONF_AUTO_FAN_MODE = "auto_fan_mode"
CONF_AUTO_FAN_NONE = "auto_fan_none"
CONF_AUTO_FAN_LOW = "auto_fan_low"
CONF_AUTO_FAN_MEDIUM = "auto_fan_medium"
CONF_AUTO_FAN_HIGH = "auto_fan_high"
CONF_AUTO_FAN_TURBO = "auto_fan_turbo"
DEFAULT_SHORT_EMA_PARAMS = {
"max_alpha": 0.5,
@@ -106,6 +113,7 @@ DEFAULT_SHORT_EMA_PARAMS = {
CONF_PRESETS = {
p: f"{p}_temp"
for p in (
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -115,6 +123,7 @@ CONF_PRESETS = {
CONF_PRESETS_WITH_AC = {
p: f"{p}_temp"
for p in (
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -130,6 +139,7 @@ PRESET_AWAY_SUFFIX = "_away"
CONF_PRESETS_AWAY = {
p: f"{p}_temp"
for p in (
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX,
PRESET_ECO + PRESET_AWAY_SUFFIX,
PRESET_COMFORT + PRESET_AWAY_SUFFIX,
PRESET_BOOST + PRESET_AWAY_SUFFIX,
@@ -139,6 +149,7 @@ CONF_PRESETS_AWAY = {
CONF_PRESETS_AWAY_WITH_AC = {
p: f"{p}_temp"
for p in (
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX,
PRESET_ECO + PRESET_AWAY_SUFFIX,
PRESET_COMFORT + PRESET_AWAY_SUFFIX,
PRESET_BOOST + PRESET_AWAY_SUFFIX,
@@ -148,7 +159,12 @@ CONF_PRESETS_AWAY_WITH_AC = {
)
}
CONF_PRESETS_SELECTIONABLE = [PRESET_ECO, PRESET_COMFORT, PRESET_BOOST]
CONF_PRESETS_SELECTIONABLE = [
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
]
CONF_PRESETS_VALUES = list(CONF_PRESETS.values())
CONF_PRESETS_AWAY_VALUES = list(CONF_PRESETS_AWAY.values())
@@ -207,6 +223,7 @@ ALL_CONF = (
CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN,
CONF_INVERSE_SWITCH,
CONF_AUTO_FAN_MODE,
]
+ CONF_PRESETS_VALUES
+ CONF_PRESETS_AWAY_VALUES
@@ -233,6 +250,14 @@ CONF_THERMOSTAT_TYPES = [
CONF_THERMOSTAT_VALVE,
]
CONF_AUTO_FAN_MODES = [
CONF_AUTO_FAN_NONE,
CONF_AUTO_FAN_LOW,
CONF_AUTO_FAN_MEDIUM,
CONF_AUTO_FAN_HIGH,
CONF_AUTO_FAN_TURBO,
]
SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE
SERVICE_SET_PRESENCE = "set_presence"
@@ -240,6 +265,7 @@ SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
SERVICE_SET_SECURITY = "set_security"
SERVICE_SET_WINDOW_BYPASS = "set_window_bypass"
SERVICE_SET_AUTO_REGULATION_MODE = "set_auto_regulation_mode"
SERVICE_SET_AUTO_FAN_MODE = "set_auto_fan_mode"
DEFAULT_SECURITY_MIN_ON_PERCENT = 0.5
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
@@ -247,6 +273,9 @@ DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
ATTR_TOTAL_ENERGY = "total_energy"
ATTR_MEAN_POWER_CYCLE = "mean_cycle_power"
AUTO_FAN_DTEMP_THRESHOLD = 2
AUTO_FAN_DEACTIVATED_MODES = ["mute", "auto", "low"]
# A special regulation parameter suggested by @Maia here: https://github.com/jmcollin78/versatile_thermostat/discussions/154
class RegulationParamSlow:
@@ -14,6 +14,6 @@
"quality_scale": "silver",
"requirements": [],
"ssdp": [],
"version": "4.2.0",
"version": "4.3.0",
"zeroconf": []
}
@@ -43,6 +43,7 @@ set_preset_temperature:
- "eco"
- "comfort"
- "boost"
- "frost"
- "eco_ac"
- "comfort_ac"
- "boost_ac"
@@ -161,3 +162,25 @@ set_auto_regulation_mode:
- "Strong"
- "Slow"
- "Expert"
set_auto_fan_mode:
name: Set Auto Fan mode
description: Change the mode of auto-fan (only for VTherm over climate)
target:
entity:
integration: versatile_thermostat
fields:
auto_fan_mode:
name: Auto fan mode
description: Possible values
required: true
advanced: false
default: true
selector:
select:
options:
- "None"
- "Low"
- "Medium"
- "High"
- "Turbo"
@@ -25,24 +25,25 @@
"title": "Linked entities",
"description": "Linked entities attributes",
"data": {
"heater_entity_id": "1rst heater switch",
"heater_entity_id": "1st heater switch",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"proportional_function": "Algorithm",
"climate_entity_id": "1rst underlying climate",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode",
"valve_entity_id": "1rst valve number",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Mandatory heater entity id",
@@ -55,14 +56,15 @@
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1rst valve number entity id",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command"
"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"
}
},
"tpi": {
@@ -75,11 +77,21 @@
},
"presets": {
"title": "Presets",
"description": "For each presets, give the target temperature (0 to ignore preset)",
"description": "For each preset set the target temperature (0 to ignore preset)",
"data": {
"eco_temp": "Eco preset",
"comfort_temp": "Comfort preset",
"boost_temp": "Boost preset",
"frost_temp": "Frost protection preset",
"eco_ac_temp": "Eco preset for AC mode",
"comfort_ac_temp": "Comfort preset for AC mode",
"boost_ac_temp": "Boost preset for AC mode"
},
"data_description": {
"eco_temp": "Temperature in Eco preset",
"comfort_temp": "Temperature in Comfort preset",
"boost_temp": "Temperature in Boost preset",
"frost_temp": "Temperature in Frost protection preset",
"eco_ac_temp": "Temperature in Eco preset for AC mode",
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
"boost_ac_temp": "Temperature in Boost preset for AC mode"
@@ -96,16 +108,16 @@
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
},
"data_description": {
"window_sensor_entity_id": "Leave empty if no window sensor should be use",
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
"window_delay": "The delay in seconds before sensor detection is taken into account",
"window_auto_open_threshold": "Recommended value: between 3 and 10. Leave empty if automatic window open detection is not use",
"window_auto_close_threshold": "Recommended value: 0. Leave empty if automatic window open detection is not use",
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not use"
"window_auto_open_threshold": "Recommended value: between 3 and 10. Leave empty if automatic window open detection is not used",
"window_auto_close_threshold": "Recommended value: 0. Leave empty if automatic window open detection is not used",
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used"
}
},
"motion": {
"title": "Motion management",
"description": "Motion sensor management. Preset can switch automatically depending of a motion detection\nLeave corresponding entity_id empty if not used.\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\nLeave corresponding entity_id empty if not used.\nmotion_preset and no_motion_preset should be set to the corresponding preset name",
"data": {
"motion_sensor_entity_id": "Motion sensor entity id",
"motion_delay": "Activation delay",
@@ -115,7 +127,7 @@
},
"data_description": {
"motion_sensor_entity_id": "The entity id of the motion sensor",
"motion_delay": "Motion activation activation delay (seconds)",
"motion_delay": "Motion activation delay (seconds)",
"motion_off_delay": "Motion deactivation delay (seconds)",
"motion_preset": "Preset to use when motion is detected",
"no_motion_preset": "Preset to use when no motion is detected"
@@ -134,10 +146,21 @@
"title": "Presence management",
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present).\nThen specify either the preset to use when presence sensor is false or the offset in temperature to apply.\nIf preset is given, the offset will not be used.\nLeave corresponding entity_id empty if not used.",
"data": {
"presence_sensor_entity_id": "Presence sensor",
"eco_away_temp": "Eco preset",
"comfort_away_temp": "Comfort preset",
"boost_away_temp": "Boost preset",
"frost_away_temp": "Frost protection preset",
"eco_ac_away_temp": "Eco preset in AC mode",
"comfort_ac_away_temp": "Comfort preset in AC mode",
"boost_ac_away_temp": "Boost pres et in AC mode"
},
"data_description": {
"presence_sensor_entity_id": "Presence sensor entity id",
"eco_away_temp": "Temperature in Eco preset when no presence",
"comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence",
"frost_away_temp": "Temperature in Frost protection preset when no presence",
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode"
@@ -145,7 +168,7 @@
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or 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.",
"data": {
"minimal_activation_delay": "Minimal activation delay",
"security_delay_min": "Security delay (in minutes)",
@@ -154,16 +177,16 @@
},
"data_description": {
"minimal_activation_delay": "Delay in seconds under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a security off state",
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a security off state",
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of power percent the thermostat won't go into security preset",
"security_default_on_percent": "The default heating power percent value in security preset. Set to 0 to switch off heater in security present"
"security_default_on_percent": "The default heating power percent value in security preset. Set to 0 to switch off heater in security preset"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id",
"window_open_detection_method": "Only one window open detection method should be used. Use sensor or automatic detection through temperature threshold but not both"
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
},
"abort": {
"already_configured": "Device is already configured"
@@ -194,24 +217,25 @@
"title": "Linked entities",
"description": "Linked entities attributes",
"data": {
"heater_entity_id": "1rst heater switch",
"heater_entity_id": "1st heater switch",
"heater_entity2_id": "2nd heater switch",
"heater_entity3_id": "3rd heater switch",
"heater_entity4_id": "4th heater switch",
"proportional_function": "Algorithm",
"climate_entity_id": "1rst underlying climate",
"climate_entity_id": "1st underlying climate",
"climate_entity2_id": "2nd underlying climate",
"climate_entity3_id": "3rd underlying climate",
"climate_entity4_id": "4th underlying climate",
"ac_mode": "AC mode",
"valve_entity_id": "1rst valve number",
"valve_entity_id": "1st valve number",
"valve_entity2_id": "2nd valve number",
"valve_entity3_id": "3rd valve number",
"valve_entity4_id": "4th valve number",
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Mandatory heater entity id",
@@ -224,14 +248,15 @@
"climate_entity3_id": "3rd underlying climate entity id",
"climate_entity4_id": "4th underlying climate entity id",
"ac_mode": "Use the Air Conditioning (AC) mode",
"valve_entity_id": "1rst valve number entity id",
"valve_entity_id": "1st valve number entity id",
"valve_entity2_id": "2nd valve number entity id",
"valve_entity3_id": "3rd valve number entity id",
"valve_entity4_id": "4th valve number entity id",
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command"
"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"
}
},
"tpi": {
@@ -244,11 +269,21 @@
},
"presets": {
"title": "Presets",
"description": "For each presets, give the target temperature (0 to ignore preset)",
"description": "For each preset set the target temperature (0 to ignore preset)",
"data": {
"eco_temp": "Eco preset",
"comfort_temp": "Comfort preset",
"boost_temp": "Boost preset",
"frost_temp": "Frost protection preset",
"eco_ac_temp": "Eco preset for AC mode",
"comfort_ac_temp": "Comfort preset for AC mode",
"boost_ac_temp": "Boost preset for AC mode"
},
"data_description": {
"eco_temp": "Temperature in Eco preset",
"comfort_temp": "Temperature in Comfort preset",
"boost_temp": "Temperature in Boost preset",
"frost_temp": "Temperature in Frost protection preset",
"eco_ac_temp": "Temperature in Eco preset for AC mode",
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
"boost_ac_temp": "Temperature in Boost preset for AC mode"
@@ -265,11 +300,11 @@
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
},
"data_description": {
"window_sensor_entity_id": "Leave empty if no window sensor should be use",
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
"window_delay": "The delay in seconds before sensor detection is taken into account",
"window_auto_open_threshold": "Recommended value: between 3 and 10. Leave empty if automatic window open detection is not use",
"window_auto_close_threshold": "Recommended value: 0. Leave empty if automatic window open detection is not use",
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not use"
"window_auto_open_threshold": "Recommended value: between 3 and 10. Leave empty if automatic window open detection is not used",
"window_auto_close_threshold": "Recommended value: 0. Leave empty if automatic window open detection is not used",
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used"
}
},
"motion": {
@@ -284,7 +319,7 @@
},
"data_description": {
"motion_sensor_entity_id": "The entity id of the motion sensor",
"motion_delay": "Motion activation activation delay (seconds)",
"motion_delay": "Motion activation delay (seconds)",
"motion_off_delay": "Motion deactivation delay (seconds)",
"motion_preset": "Preset to use when motion is detected",
"no_motion_preset": "Preset to use when no motion is detected"
@@ -303,7 +338,7 @@
"title": "Presence management",
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present).\nThen specify either the preset to use when presence sensor is false or the offset in temperature to apply.\nIf preset is given, the offset will not be used.\nLeave corresponding entity_id empty if not used.",
"data": {
"presence_sensor_entity_id": "Presence sensor entity id (true is present)",
"presence_sensor_entity_id": "Presence sensor entity id",
"eco_away_temp": "Temperature in Eco preset when no presence",
"comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence",
@@ -314,25 +349,25 @@
},
"advanced": {
"title": "Advanced parameters",
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or 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.",
"data": {
"minimal_activation_delay": "Minimal activation delay",
"security_delay_min": "Security delay (in minutes)",
"security_min_on_percent": "Minimal power percent for security mode",
"security_min_on_percent": "Minimal power percent to enable security mode",
"security_default_on_percent": "Power percent to use in security mode"
},
"data_description": {
"minimal_activation_delay": "Delay in seconds under which the equipment will not be activated",
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a security off state",
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a security off state",
"security_min_on_percent": "Minimal heating percent value for security preset activation. Below this amount of power percent the thermostat won't go into security preset",
"security_default_on_percent": "The default heating power percent value in security preset. Set to 0 to switch off heater in security present"
"security_default_on_percent": "The default heating power percent value in security preset. Set to 0 to switch off heater in security preset"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id",
"window_open_detection_method": "Only one window open detection method should be used. Use sensor or automatic detection through temperature threshold but not both"
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
},
"abort": {
"already_configured": "Device is already configured"
@@ -355,6 +390,15 @@
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "No auto fan",
"auto_fan_low": "Low",
"auto_fan_medium": "Medium",
"auto_fan_high": "High",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
@@ -1,4 +1,4 @@
# pylint: disable=line-too-long
# pylint: disable=line-too-long, too-many-lines
""" A climate over switch classe """
import logging
from datetime import timedelta, datetime
@@ -9,7 +9,11 @@ from homeassistant.helpers.event import (
async_track_time_interval,
)
from homeassistant.components.climate import HVACAction, HVACMode
from homeassistant.components.climate import (
HVACAction,
HVACMode,
ClimateEntityFeature,
)
from .commons import NowClass, round_to_nearest
from .base_thermostat import BaseThermostat
@@ -31,10 +35,19 @@ from .const import (
CONF_AUTO_REGULATION_EXPERT,
CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN,
CONF_AUTO_FAN_MODE,
CONF_AUTO_FAN_NONE,
CONF_AUTO_FAN_LOW,
CONF_AUTO_FAN_MEDIUM,
CONF_AUTO_FAN_HIGH,
CONF_AUTO_FAN_TURBO,
RegulationParamSlow,
RegulationParamLight,
RegulationParamMedium,
RegulationParamStrong,
AUTO_FAN_DTEMP_THRESHOLD,
AUTO_FAN_DEACTIVATED_MODES,
UnknownEntity,
)
from .vtherm_api import VersatileThermostatAPI
@@ -52,6 +65,13 @@ class ThermostatOverClimate(BaseThermostat):
_auto_regulation_dtemp: float = None
_auto_regulation_period_min: int = None
_last_regulation_change: datetime = None
# The fan mode configured in configEntry
_auto_fan_mode: str = None
# The current fan mode (could be change by service call)
_current_auto_fan_mode: str = None
# The fan_mode name depending of the current_mode
_auto_activated_fan_mode: str = None
_auto_deactivated_fan_mode: str = None
_entity_component_unrecorded_attributes = (
BaseThermostat._entity_component_unrecorded_attributes.union(
@@ -65,6 +85,10 @@ class ThermostatOverClimate(BaseThermostat):
"underlying_climate_3",
"regulation_accumulated_error",
"auto_regulation_mode",
"auto_fan_mode",
"current_auto_fan_mode",
"auto_activated_fan_mode",
"auto_deactivated_fan_mode",
}
)
)
@@ -164,6 +188,51 @@ class ThermostatOverClimate(BaseThermostat):
self.regulated_target_temp, self._attr_max_temp, self._attr_min_temp
)
async def _send_auto_fan_mode(self):
"""Send the fan mode if auto_fan_mode and temperature gap is > threshold"""
if not self._auto_fan_mode or not self._auto_activated_fan_mode:
return
dtemp = (
self.regulated_target_temp if self.is_regulated else self.target_temperature
)
if dtemp is None or self.current_temperature is None:
return
dtemp = dtemp - self.current_temperature
should_activate_auto_fan = (
dtemp >= AUTO_FAN_DTEMP_THRESHOLD or dtemp <= -AUTO_FAN_DTEMP_THRESHOLD
)
# deal with ac / non ac mode
hvac_mode = self.hvac_mode
if (
(hvac_mode == HVACMode.COOL and dtemp > 0)
or (hvac_mode == HVACMode.HEAT and dtemp < 0)
or (hvac_mode == HVACMode.OFF)
):
should_activate_auto_fan = False
if should_activate_auto_fan and self.fan_mode != self._auto_activated_fan_mode:
_LOGGER.info(
"%s - Activate the auto fan mode with %s because delta temp is %.2f",
self,
self._auto_fan_mode,
dtemp,
)
await self.async_set_fan_mode(self._auto_activated_fan_mode)
if (
not should_activate_auto_fan
and self.fan_mode not in AUTO_FAN_DEACTIVATED_MODES
):
_LOGGER.info(
"%s - DeActivate the auto fan mode with %s because delta temp is %.2f",
self,
self._auto_deactivated_fan_mode,
dtemp,
)
await self.async_set_fan_mode(self._auto_deactivated_fan_mode)
@overrides
def post_init(self, entry_infos):
"""Initialize the Thermostat"""
@@ -201,6 +270,12 @@ class ThermostatOverClimate(BaseThermostat):
else 5
)
self._auto_fan_mode = (
entry_infos.get(CONF_AUTO_FAN_MODE)
if entry_infos.get(CONF_AUTO_FAN_MODE) is not None
else CONF_AUTO_FAN_NONE
)
def choose_auto_regulation_mode(self, auto_regulation_mode):
"""Choose or change the regulation mode"""
self._auto_regulation_mode = auto_regulation_mode
@@ -277,6 +352,50 @@ class ThermostatOverClimate(BaseThermostat):
self.target_temperature, 0, 0, 0, 0, 0.1, 0
)
def choose_auto_fan_mode(self, auto_fan_mode):
"""Choose the correct fan mode depending of the underlying capacities and the configuration"""
self._current_auto_fan_mode = auto_fan_mode
# Get the supported feature of the first underlying. We suppose each underlying have the same fan attributes
fan_supported = self.supported_features & ClimateEntityFeature.FAN_MODE > 0
if auto_fan_mode == CONF_AUTO_FAN_NONE or not fan_supported:
self._auto_activated_fan_mode = self._auto_deactivated_fan_mode = None
return
def find_fan_mode(fan_modes, fan_mode) -> str:
"""Return the fan_mode if it exist of None if not"""
try:
return fan_mode if fan_modes.index(fan_mode) >= 0 else None
except ValueError:
return None
fan_modes = self.fan_modes
if auto_fan_mode == CONF_AUTO_FAN_LOW:
self._auto_activated_fan_mode = find_fan_mode(fan_modes, "low")
elif auto_fan_mode == CONF_AUTO_FAN_MEDIUM:
self._auto_activated_fan_mode = find_fan_mode(fan_modes, "mid")
elif auto_fan_mode == CONF_AUTO_FAN_HIGH:
self._auto_activated_fan_mode = find_fan_mode(fan_modes, "high")
elif auto_fan_mode == CONF_AUTO_FAN_TURBO:
self._auto_activated_fan_mode = find_fan_mode(
fan_modes, "turbo"
) or find_fan_mode(fan_modes, "high")
for val in AUTO_FAN_DEACTIVATED_MODES:
if find_fan_mode(fan_modes, val):
self._auto_deactivated_fan_mode = val
break
_LOGGER.info(
"%s - choose_auto_fan_mode founds current_auto_fan_mode=%s auto_activated_fan_mode=%s and auto_deactivated_fan_mode=%s",
self,
self._current_auto_fan_mode,
self._auto_activated_fan_mode,
self._auto_deactivated_fan_mode,
)
@overrides
async def async_added_to_hass(self):
"""Run when entity about to be added."""
@@ -302,6 +421,9 @@ class ThermostatOverClimate(BaseThermostat):
)
)
# init auto_regulation_mode
self.choose_auto_regulation_mode(self._auto_regulation_mode)
@overrides
def restore_specific_previous_state(self, old_state):
"""Restore my specific attributes from previous state"""
@@ -348,6 +470,19 @@ class ThermostatOverClimate(BaseThermostat):
"regulation_accumulated_error"
] = self._regulation_algo.accumulated_error
self._attr_extra_state_attributes["auto_fan_mode"] = self.auto_fan_mode
self._attr_extra_state_attributes[
"current_auto_fan_mode"
] = self._current_auto_fan_mode
self._attr_extra_state_attributes[
"auto_activated_fan_mode"
] = self._auto_activated_fan_mode
self._attr_extra_state_attributes[
"auto_deactivated_fan_mode"
] = self._auto_deactivated_fan_mode
self.async_write_ha_state()
_LOGGER.debug(
"%s - Calling update_custom_attributes: %s",
@@ -435,6 +570,12 @@ class ThermostatOverClimate(BaseThermostat):
else None
)
new_fan_mode = (
new_state.attributes.get("fan_mode")
if new_state and new_state.attributes
else None
)
old_state_date_changed = (
old_state.last_changed if old_state and old_state.last_changed else None
)
@@ -545,6 +686,11 @@ class ThermostatOverClimate(BaseThermostat):
for under in self._underlyings:
await under.set_hvac_mode(new_hvac_mode)
# A quick win to known if it has change by using the self._attr_fan_mode and not only underlying[0].fan_mode
if new_fan_mode != self._attr_fan_mode:
self._attr_fan_mode = new_fan_mode
changes = True
if not changes:
# try to manage new target temperature set if state
_LOGGER.debug(
@@ -576,6 +722,9 @@ class ThermostatOverClimate(BaseThermostat):
await self._send_regulated_temperature()
if self._auto_fan_mode and self._auto_fan_mode != CONF_AUTO_FAN_NONE:
await self._send_auto_fan_mode()
return ret
@property
@@ -583,6 +732,11 @@ class ThermostatOverClimate(BaseThermostat):
"""Get the regulation mode"""
return self._auto_regulation_mode
@property
def auto_fan_mode(self):
"""Get the auto fan mode"""
return self._auto_fan_mode
@property
def regulated_target_temp(self):
"""Get the regulated target temperature"""
@@ -613,7 +767,8 @@ class ThermostatOverClimate(BaseThermostat):
Requires ClimateEntityFeature.FAN_MODE.
"""
if self.underlying_entity(0):
return self.underlying_entity(0).fan_mode
self._attr_fan_mode = self.underlying_entity(0).fan_mode
return self._attr_fan_mode
return None
@@ -707,6 +862,31 @@ class ThermostatOverClimate(BaseThermostat):
return None
@property
def is_initialized(self) -> bool:
"""Check if all underlyings are initialized"""
for under in self._underlyings:
if not under.is_initialized:
return False
return True
@overrides
def init_underlyings(self):
"""Init the underlyings if not already done"""
for under in self._underlyings:
if not under.is_initialized:
_LOGGER.info(
"%s - Underlying %s is not initialized. Try to initialize it",
self,
under.entity_id,
)
try:
under.startup()
except UnknownEntity:
# still not found, we an stop here
return False
self.choose_auto_fan_mode(self._auto_fan_mode)
@overrides
def turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
@@ -795,3 +975,29 @@ class ThermostatOverClimate(BaseThermostat):
await self._send_regulated_temperature()
self.update_custom_attributes()
async def service_set_auto_fan_mode(self, auto_fan_mode):
"""Called by a service call:
service: versatile_thermostat.set_auto_fan_mode
data:
auto_fan_mode: [None | Low | Medium | High | Turbo]
target:
entity_id: climate.thermostat_1
"""
_LOGGER.info(
"%s - Calling service_set_auto_fan_mode, auto_fan_mode: %s",
self,
auto_fan_mode,
)
if auto_fan_mode == "None":
self.choose_auto_fan_mode(CONF_AUTO_FAN_NONE)
elif auto_fan_mode == "Low":
self.choose_auto_fan_mode(CONF_AUTO_FAN_LOW)
elif auto_fan_mode == "Medium":
self.choose_auto_fan_mode(CONF_AUTO_FAN_MEDIUM)
elif auto_fan_mode == "High":
self.choose_auto_fan_mode(CONF_AUTO_FAN_HIGH)
elif auto_fan_mode == "Turbo":
self.choose_auto_fan_mode(CONF_AUTO_FAN_TURBO)
self.update_custom_attributes()
@@ -42,7 +42,8 @@
"auto_regulation_mode": "Αυτόματη ρύθμιση",
"auto_regulation_dtemp": "Όριο ρύθμισης",
"auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης",
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη"
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα",
@@ -62,7 +63,8 @@
"auto_regulation_mode": "Αυτόματη προσαρμογή της στοχευμένης θερμοκρασίας",
"auto_regulation_dtemp": "Το όριο σε ° κάτω από το οποίο η αλλαγή θερμοκρασίας δεν θα αποστέλλεται",
"auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης",
"inverse_switch_command": "Για διακόπτη με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστρέψετε την εντολή"
"inverse_switch_command": "Για διακόπτη με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστρέψετε την εντολή",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
}
},
"tpi": {
@@ -80,6 +82,7 @@
"eco_temp": "Θερμοκρασία στο προκαθορισμένο Eco",
"comfort_temp": "Θερμοκρασία στο προκαθορισμένο Comfort",
"boost_temp": "Θερμοκρασία στο προκαθορισμένο Boost",
"frost_temp": "Θερμοκρασία στο προκαθορισμένο Frost protection",
"eco_ac_temp": "Θερμοκρασία στο προκαθορισμένο Eco για λειτουργία AC",
"comfort_ac_temp": "Θερμοκρασία στο προκαθορισμένο Comfort για λειτουργία AC",
"boost_ac_temp": "Θερμοκρασία στο προκαθορισμένο Boost για λειτουργία AC"
@@ -138,6 +141,7 @@
"eco_away_temp": "Θερμοκρασία στο προκαθορισμένο Eco όταν δεν υπάρχει παρουσία",
"comfort_away_temp": "Θερμοκρασία στο προκαθορισμένο Comfort όταν δεν υπάρχει παρουσία",
"boost_away_temp": "Θερμοκρασία στο προκαθορισμένο Boost όταν δεν υπάρχει παρουσία",
"frost_away_temp": "Θερμοκρασία στο προκαθορισμένο Frost protection όταν δεν υπάρχει παρουσία",
"eco_ac_away_temp": "Θερμοκρασία στο προκαθορισμένο Eco όταν δεν υπάρχει παρουσία σε λειτουργία AC",
"comfort_ac_away_temp": "Θερμοκρασία στο προκαθορισμένο Comfort όταν δεν υπάρχει παρουσία σε λειτουργία AC",
"boost_ac_away_temp": "Θερμοκρασία στο προκαθορισμένο Boost όταν δεν υπάρχει παρουσία σε λειτουργία AC"
@@ -211,7 +215,8 @@
"auto_regulation_mode": "Αυτορύθμιση",
"auto_regulation_dtemp": "Όριο ρύθμισης",
"auto_regulation_periode_min": "Ελάχιστη περίοδος ρύθμισης",
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη"
"inverse_switch_command": "Αντίστροφη εντολή διακόπτη",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Υποχρεωτική ταυτότητα οντότητας θερμαντήρα",
@@ -231,7 +236,8 @@
"auto_regulation_mode": "Αυτόματη ρύθμιση της στοχευόμενης θερμοκρασίας",
"auto_regulation_dtemp": "Το κατώφλι σε °C κάτω από το οποίο η αλλαγή της θερμοκρασίας δεν θα αποστέλλεται",
"auto_regulation_periode_min": "Διάρκεια σε λεπτά μεταξύ δύο ενημερώσεων ρύθμισης",
"inverse_switch_command": "Για διακόπτες με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστραφεί η εντολή"
"inverse_switch_command": "Για διακόπτες με πιλοτικό καλώδιο και δίοδο μπορεί να χρειαστεί να αντιστραφεί η εντολή",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
}
},
"tpi": {
@@ -249,6 +255,7 @@
"eco_temp": "Θερμοκρασία στην οικονομική προεπιλογή",
"comfort_temp": "Θερμοκρασία στην άνετη προεπιλογή",
"boost_temp": "Θερμοκρασία στην ενισχυμένη προεπιλογή",
"frost_temp": "Θερμοκρασία στο προκαθορισμένο Frost protection",
"eco_ac_temp": "Θερμοκρασία στην οικονομική προεπιλογή για τη λειτουργία AC",
"comfort_ac_temp": "Θερμοκρασία στην άνετη προεπιλογή για τη λειτουργία AC",
"boost_ac_temp": "Θερμοκρασία στην ενισχυμένη προεπιλογή για τη λειτουργία AC"
@@ -307,6 +314,7 @@
"eco_away_temp": "Θερμοκρασία στο πρόγραμμα Eco όταν δεν υπάρχει παρουσία",
"comfort_away_temp": "Θερμοκρασία στο πρόγραμμα Comfort όταν δεν υπάρχει παρουσία",
"boost_away_temp": "Θερμοκρασία στο πρόγραμμα Boost όταν δεν υπάρχει παρουσία",
"frost_away_temp": "Θερμοκρασία στο προκαθορισμένο Frost protection όταν δεν υπάρχει παρουσία",
"eco_ac_away_temp": "Θερμοκρασία στο πρόγραμμα Eco όταν δεν υπάρχει παρουσία σε λειτουργία AC",
"comfort_ac_away_temp": "Θερμοκρασία στο πρόγραμμα Comfort όταν δεν υπάρχει παρουσία σε λειτουργία AC",
"boost_ac_away_temp": "Θερμοκρασία στο πρόγραμμα Boost όταν δεν υπάρχει παρουσία σε λειτουργία AC"
@@ -355,6 +363,15 @@
"auto_regulation_expert": "Εμπειρογνώμων",
"auto_regulation_none": "Χωρίς αυτόματη ρύθμιση"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "No auto fan",
"auto_fan_low": "Low",
"auto_fan_medium": "Medium",
"auto_fan_high": "High",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
@@ -42,7 +42,8 @@
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Mandatory heater entity id",
@@ -62,7 +63,8 @@
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command"
"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"
}
},
"tpi": {
@@ -77,9 +79,19 @@
"title": "Presets",
"description": "For each preset set the target temperature (0 to ignore preset)",
"data": {
"eco_temp": "Eco preset",
"comfort_temp": "Comfort preset",
"boost_temp": "Boost preset",
"frost_temp": "Frost protection preset",
"eco_ac_temp": "Eco preset for AC mode",
"comfort_ac_temp": "Comfort preset for AC mode",
"boost_ac_temp": "Boost preset for AC mode"
},
"data_description": {
"eco_temp": "Temperature in Eco preset",
"comfort_temp": "Temperature in Comfort preset",
"boost_temp": "Temperature in Boost preset",
"frost_temp": "Temperature in Frost protection preset",
"eco_ac_temp": "Temperature in Eco preset for AC mode",
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
"boost_ac_temp": "Temperature in Boost preset for AC mode"
@@ -134,10 +146,21 @@
"title": "Presence management",
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present).\nThen specify either the preset to use when presence sensor is false or the offset in temperature to apply.\nIf preset is given, the offset will not be used.\nLeave corresponding entity_id empty if not used.",
"data": {
"presence_sensor_entity_id": "Presence sensor",
"eco_away_temp": "Eco preset",
"comfort_away_temp": "Comfort preset",
"boost_away_temp": "Boost preset",
"frost_away_temp": "Frost protection preset",
"eco_ac_away_temp": "Eco preset in AC mode",
"comfort_ac_away_temp": "Comfort preset in AC mode",
"boost_ac_away_temp": "Boost pres et in AC mode"
},
"data_description": {
"presence_sensor_entity_id": "Presence sensor entity id",
"eco_away_temp": "Temperature in Eco preset when no presence",
"comfort_away_temp": "Temperature in Comfort preset when no presence",
"boost_away_temp": "Temperature in Boost preset when no presence",
"frost_away_temp": "Temperature in Frost protection preset when no presence",
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode"
@@ -211,7 +234,8 @@
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Mandatory heater entity id",
@@ -231,7 +255,8 @@
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be sent",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to invert the command"
"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"
}
},
"tpi": {
@@ -246,9 +271,19 @@
"title": "Presets",
"description": "For each preset set the target temperature (0 to ignore preset)",
"data": {
"eco_temp": "Eco preset",
"comfort_temp": "Comfort preset",
"boost_temp": "Boost preset",
"frost_temp": "Frost protection preset",
"eco_ac_temp": "Eco preset for AC mode",
"comfort_ac_temp": "Comfort preset for AC mode",
"boost_ac_temp": "Boost preset for AC mode"
},
"data_description": {
"eco_temp": "Temperature in Eco preset",
"comfort_temp": "Temperature in Comfort preset",
"boost_temp": "Temperature in Boost preset",
"frost_temp": "Temperature in Frost protection preset",
"eco_ac_temp": "Temperature in Eco preset for AC mode",
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
"boost_ac_temp": "Temperature in Boost preset for AC mode"
@@ -355,6 +390,15 @@
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "No auto fan",
"auto_fan_low": "Low",
"auto_fan_medium": "Medium",
"auto_fan_high": "High",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
@@ -42,7 +42,8 @@
"auto_regulation_mode": "Auto-régulation",
"auto_regulation_dtemp": "Seuil de régulation",
"auto_regulation_periode_min": "Période minimale de régulation",
"inverse_switch_command": "Inverser la commande"
"inverse_switch_command": "Inverser la commande",
"auto_fan_mode": " Auto ventilation mode"
},
"data_description": {
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
@@ -62,7 +63,8 @@
"auto_regulation_mode": "Ajustement automatique de la température cible",
"auto_regulation_dtemp": "Le seuil en ° au-dessous duquel la régulation ne sera pas envoyée",
"auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation",
"inverse_switch_command": "Inverse la commande du switch pour une installation avec fil pilote et diode"
"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"
}
},
"tpi": {
@@ -77,9 +79,19 @@
"title": "Presets",
"description": "Pour chaque preset, donnez la température cible (0 pour ignorer le preset)",
"data": {
"eco_temp": "Preset Eco",
"comfort_temp": "Preset Comfort",
"boost_temp": "Preset Boost",
"frost_temp": "Preset Hors-gel",
"eco_ac_temp": "Preset Eco en mode AC",
"comfort_ac_temp": "Preset Comfort en mode AC",
"boost_ac_temp": "Preset Boost en mode AC"
},
"data_description": {
"eco_temp": "Température en preset Eco",
"comfort_temp": "Température en preset Comfort",
"boost_temp": "Température en preset Boost",
"frost_temp": "Température en preset Hors-gel",
"eco_ac_temp": "Température en preset Eco en mode AC",
"comfort_ac_temp": "Température en preset Comfort en mode AC",
"boost_ac_temp": "Température en preset Boost en mode AC"
@@ -134,10 +146,21 @@
"title": "Gestion de la présence",
"description": "Donnez un capteur de présence (true si quelqu'un est présent).\nEnsuite spécifiez soit un preset à utiliser, soit un offset de température à appliquer lorsque personne n'est présent.\nSi le préset est utilisé, l'offset ne sera pas pris en compte.\nLaissez l'entity id vide si la gestion de la présence est non utilisée.",
"data": {
"presence_sensor_entity_id": "Capteur de présence",
"eco_away_temp": "preset Eco",
"comfort_away_temp": "preset Comfort",
"boost_away_temp": "preset Boost",
"frost_away_temp": "preset Hors-gel",
"eco_ac_away_temp": "preset Eco en mode AC",
"comfort_ac_away_temp": "preset Comfort en mode AC",
"boost_ac_away_temp": "preset Boost en mode AC"
},
"data_description": {
"presence_sensor_entity_id": "Capteur de présence entity id (true si quelqu'un est présent)",
"eco_away_temp": "Température en preset Eco en cas d'absence",
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
"boost_away_temp": "Température en preset Boost en cas d'absence",
"frost_away_temp": "Température en preset Hors-gel en cas d'absence",
"eco_ac_away_temp": "Température en preset Eco en cas d'absence en mode AC",
"comfort_ac_away_temp": "Température en preset Comfort en cas d'absence en mode AC",
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC"
@@ -212,7 +235,8 @@
"auto_regulation_mode": "Auto-regulation",
"auto_regulation_dtemp": "Seuil de régulation",
"auto_regulation_periode_min": "Période minimale de régulation",
"inverse_switch_command": "Inverser la commande"
"inverse_switch_command": "Inverser la commande",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
@@ -232,7 +256,8 @@
"auto_regulation_mode": "Ajustement automatique de la consigne",
"auto_regulation_dtemp": "Le seuil en ° au-dessous duquel la régulation ne sera pas envoyée",
"auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation",
"inverse_switch_command": "Inverse la commande du switch pour une installation avec fil pilote et diode"
"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"
}
},
"tpi": {
@@ -247,9 +272,19 @@
"title": "Presets",
"description": "Pour chaque preset, donnez la température cible (0 pour ignorer le preset)",
"data": {
"eco_temp": "Preset Eco",
"comfort_temp": "Preset Comfort",
"boost_temp": "Preset Boost",
"frost_temp": "Preset Hors-gel",
"eco_ac_temp": "Preset Eco en mode AC",
"comfort_ac_temp": "Preset Comfort en mode AC",
"boost_ac_temp": "Preset Boost en mode AC"
},
"data_description": {
"eco_temp": "Température en preset Eco",
"comfort_temp": "Température en preset Comfort",
"boost_temp": "Température en preset Boost",
"frost_temp": "Température en preset Hors-gel",
"eco_ac_temp": "Température en preset Eco en mode AC",
"comfort_ac_temp": "Température en preset Comfort en mode AC",
"boost_ac_temp": "Température en preset Boost en mode AC"
@@ -304,10 +339,21 @@
"title": "Gestion de la présence",
"description": "Donnez un capteur de présence (true si quelqu'un est présent).\nEnsuite spécifiez soit un preset à utiliser, soit un offset de température à appliquer lorsque personne n'est présent.\nSi le préset est utilisé, l'offset ne sera pas pris en compte.\nLaissez l'entity id vide si la gestion de la présence est non utilisée.",
"data": {
"presence_sensor_entity_id": "Capteur de présence",
"eco_away_temp": "preset Eco",
"comfort_away_temp": "preset Comfort",
"boost_away_temp": "preset Boost",
"frost_away_temp": "preset Hors-gel",
"eco_ac_away_temp": "preset Eco en mode AC",
"comfort_ac_away_temp": "preset Comfort en mode AC",
"boost_ac_away_temp": "preset Boost en mode AC"
},
"data_description": {
"presence_sensor_entity_id": "Capteur de présence entity id (true si quelqu'un est présent)",
"eco_away_temp": "Température en preset Eco en cas d'absence",
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
"boost_away_temp": "Température en preset Boost en cas d'absence",
"frost_away_temp": "Température en preset Hors-gel en cas d'absence",
"eco_ac_away_temp": "Température en preset Eco en cas d'absence en mode AC",
"comfort_ac_away_temp": "Température en preset Comfort en cas d'absence en mode AC",
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC"
@@ -356,6 +402,15 @@
"auto_regulation_expert": "Expert",
"auto_regulation_none": "Aucune"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "Pas d'auto fan",
"auto_fan_low": "Faible",
"auto_fan_medium": "Moyenne",
"auto_fan_high": "Forte",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
@@ -40,7 +40,8 @@
"valve_entity3_id": "Terza valvola",
"valve_entity4_id": "Quarta valvola",
"auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Comando inverso"
"inverse_switch_command": "Comando inverso",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Entity id obbligatoria del primo riscaldatore",
@@ -58,7 +59,8 @@
"valve_entity3_id": "Entity id della terza valvola",
"valve_entity4_id": "Entity id della quarta valvola",
"auto_regulation_mode": "Regolazione automatica della temperatura target",
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo"
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
}
},
"tpi": {
@@ -76,6 +78,7 @@
"eco_temp": "Temperatura nel preset Eco",
"comfort_temp": "Temperatura nel preset Comfort",
"boost_temp": "Temperatura nel preset Boost",
"frost_temp": "Temperatura nel preset Frost protection",
"eco_ac_temp": "Temperatura nel preset Eco (AC mode)",
"comfort_ac_temp": "Temperatura nel preset Comfort (AC mode)",
"boost_ac_temp": "Temperatura nel preset Boost (AC mode)"
@@ -127,6 +130,7 @@
"eco_away_temp": "Temperatura al preset Eco in caso d'assenza",
"comfort_away_temp": "Temperatura al preset Comfort in caso d'assenza",
"boost_away_temp": "Temperatura al preset Boost in caso d'assenza",
"frost_away_temp": "Temperatura al preset Frost protection in caso d'assenza",
"eco_ac_away_temp": "Temperatura al preset Eco in caso d'assenza (AC mode)",
"comfort_ac_away_temp": "Temperatura al preset Comfort in caso d'assenza (AC mode)",
"boost_ac_away_temp": "Temperatura al preset Boost in caso d'assenza (AC mode)"
@@ -198,7 +202,8 @@
"valve_entity3_id": "Terza valvola",
"valve_entity4_id": "Quarta valvola",
"auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Comando inverso"
"inverse_switch_command": "Comando inverso",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "Entity id obbligatoria del primo riscaldatore",
@@ -216,7 +221,8 @@
"valve_entity3_id": "Entity id della terza valvola",
"valve_entity4_id": "Entity id della quarta valvola",
"auto_regulation_mode": "Autoregolamentazione",
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo"
"inverse_switch_command": "Inverte il controllo dell'interruttore per un'installazione con filo pilota e diodo",
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
}
},
"tpi": {
@@ -234,6 +240,7 @@
"eco_temp": "Temperatura nel preset Eco",
"comfort_temp": "Temperatura nel preset Comfort",
"boost_temp": "Temperatura nel preset Boost",
"frost_temp": "Temperatura nel preset Frost protection",
"eco_ac_temp": "Temperatura nel preset Eco (AC mode)",
"comfort_ac_temp": "Temperatura nel preset Comfort (AC mode)",
"boost_ac_temp": "Temperatura nel preset Boost (AC mode)"
@@ -285,6 +292,7 @@
"eco_away_temp": "Temperatura al preset Eco in caso d'assenza",
"comfort_away_temp": "Temperatura al preset Comfort in caso d'assenza",
"boost_away_temp": "Temperatura al preset Boost in caso d'assenza",
"frost_away_temp": "Temperatura al preset Frost protection in caso d'assenza",
"eco_ac_away_temp": "Temperatura al preset Eco in caso d'assenza (AC mode)",
"comfort_ac_away_temp": "Temperatura al preset Comfort in caso d'assenza (AC mode)",
"boost_ac_away_temp": "Temperatura al preset Boost in caso d'assenza (AC mode)"
@@ -333,6 +341,15 @@
"auto_regulation_expert": "Esperto",
"auto_regulation_none": "Nessuna autoregolamentazione"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "Nessune autofan",
"auto_fan_low": "Leggera",
"auto_fan_medium": "Media",
"auto_fan_high": "Forte",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
@@ -42,7 +42,8 @@
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "ID entity povinného ohrievača",
@@ -62,7 +63,8 @@
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command"
"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"
}
},
"tpi": {
@@ -80,6 +82,7 @@
"eco_temp": "Teplota v predvoľbe Eco",
"comfort_temp": "Prednastavená teplota v komfortnom režime",
"boost_temp": "Teplota v prednastavení Boost",
"frost_temp": "Teplota v prednastavení Frost protection",
"eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC",
"comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC",
"boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC"
@@ -138,6 +141,7 @@
"eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť",
"comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný",
"boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný",
"frost_away_temp": "Prednastavená teplota v režime Frost protection, keď nie je prítomný",
"eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC",
"comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC",
"boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC"
@@ -211,7 +215,8 @@
"auto_regulation_mode": "Self-regulation",
"auto_regulation_dtemp": "Regulation threshold",
"auto_regulation_periode_min": "Regulation minimal period",
"inverse_switch_command": "Inverse switch command"
"inverse_switch_command": "Inverse switch command",
"auto_fan_mode": " Auto fan mode"
},
"data_description": {
"heater_entity_id": "ID entity povinného ohrievača",
@@ -231,7 +236,8 @@
"auto_regulation_mode": "Auto adjustment of the target temperature",
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
"auto_regulation_periode_min": "Duration in minutes between two regulation update",
"inverse_switch_command": "For switch with pilot wire and diode you may need to inverse the command"
"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"
}
},
"tpi": {
@@ -249,6 +255,7 @@
"eco_temp": "Teplota v predvoľbe Eco",
"comfort_temp": "Prednastavená teplota v komfortnom režime",
"boost_temp": "Teplota v prednastavení Boost",
"frost_temp": "Teplota v prednastavení Frost protection",
"eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC",
"comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC",
"boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC"
@@ -307,6 +314,7 @@
"eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť",
"comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný",
"boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný",
"frost_away_temp": "Prednastavená teplota v režime Frost protection, keď nie je prítomný",
"eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC",
"comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC",
"boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC"
@@ -355,6 +363,15 @@
"auto_regulation_expert": "Expert",
"auto_regulation_none": "No auto-regulation"
}
},
"auto_fan_mode": {
"options": {
"auto_fan_none": "No auto-fan",
"auto_fan_low": "Low",
"auto_fan_medium": "Medium",
"auto_fan_high": "High",
"auto_fan_turbo": "Turbo"
}
}
},
"entity": {
+1 -1
View File
@@ -3,5 +3,5 @@
"content_in_root": false,
"render_readme": true,
"hide_default_branch": false,
"homeassistant": "2023.11.2"
"homeassistant": "2023.12.1"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

+1 -1
View File
@@ -1,2 +1,2 @@
homeassistant==2023.11.2
homeassistant==2023.12.1
ffmpeg
+55 -17
View File
@@ -24,7 +24,10 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.const import * # pylint: disable=wildcard-import, unused-wildcard-import
from custom_components.versatile_thermostat.underlyings import * # pylint: disable=wildcard-import, unused-wildcard-import
from custom_components.versatile_thermostat.commons import get_tz, NowClass # pylint: disable=unused-import
from custom_components.versatile_thermostat.commons import ( # pylint: disable=unused-import
get_tz,
NowClass,
)
from .const import ( # pylint: disable=unused-import
MOCK_TH_OVER_SWITCH_USER_CONFIG,
@@ -117,47 +120,80 @@ _LOGGER = logging.getLogger(__name__)
class MockClimate(ClimateEntity):
"""A Mock Climate class used for Underlying climate mode"""
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos, hvac_mode:HVACMode = HVACMode.OFF, hvac_action:HVACAction = HVACAction.OFF) -> None: # pylint: disable=unused-argument
def __init__( # pylint: disable=unused-argument, dangerous-default-value
self,
hass: HomeAssistant,
unique_id,
name,
entry_infos={},
hvac_mode: HVACMode = HVACMode.OFF,
hvac_action: HVACAction = HVACAction.OFF,
fan_modes: list[str] = None,
) -> None:
"""Initialize the thermostat."""
super().__init__()
self.hass = hass
self.platform = 'climate'
self.entity_id= self.platform+'.'+unique_id
self.platform = "climate"
self.entity_id = self.platform + "." + unique_id
self._attr_extra_state_attributes = {}
self._unique_id = unique_id
self._name = name
self._attr_hvac_action = HVACAction.OFF if hvac_mode == HVACMode.OFF else HVACAction.HEATING
self._attr_hvac_action = (
HVACAction.OFF if hvac_mode == HVACMode.OFF else HVACAction.HEATING
)
self._attr_hvac_mode = hvac_mode
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_target_temperature = 20
self._attr_current_temperature = 15
self._attr_hvac_action = hvac_action
self._fan_modes = fan_modes if fan_modes else None
self._attr_fan_mode = None
@property
def hvac_action(self):
"""The hvac action of the mock climate"""
return self._attr_hvac_action
@property
def fan_modes(self) -> list[str] | None:
"""The list of fan_modes"""
return self._fan_modes
def set_fan_mode(self, fan_mode):
"""Set the fan mode"""
self._attr_fan_mode = fan_mode
@property
def supported_features(self) -> int:
"""The supported feature of this climate entity"""
ret = ClimateEntityFeature.TARGET_TEMPERATURE
if self._fan_modes:
ret = ret | ClimateEntityFeature.FAN_MODE
return ret
def set_temperature(self, **kwargs):
""" Set the target temperature"""
"""Set the target temperature"""
temperature = kwargs.get(ATTR_TEMPERATURE)
self._attr_target_temperature = temperature
async def async_set_hvac_mode(self, hvac_mode):
""" The hvac mode"""
"""The hvac mode"""
self._attr_hvac_mode = hvac_mode
@property
def hvac_action(self):
""" The hvac action of the mock climate"""
return self._attr_hvac_action
def set_hvac_action(self, hvac_action: HVACAction):
""" Set the HVACaction """
"""Set the HVACaction"""
self._attr_hvac_action = hvac_action
class MockUnavailableClimate(ClimateEntity):
"""A Mock Climate class used for Underlying climate mode"""
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None: # pylint: disable=unused-argument
def __init__(
self, hass: HomeAssistant, unique_id, name, entry_infos
) -> None: # pylint: disable=unused-argument
"""Initialize the thermostat."""
super().__init__()
@@ -170,6 +206,8 @@ class MockUnavailableClimate(ClimateEntity):
self._attr_hvac_mode = None
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_fan_mode = None
class MagicMockClimate(MagicMock):
"""A Magic Mock class for a underlying climate entity"""
@@ -325,9 +363,7 @@ async def send_ext_temperature_change_event(
await asyncio.sleep(0.1)
async def send_power_change_event(
entity: BaseThermostat, new_power, date, sleep=True
):
async def send_power_change_event(entity: BaseThermostat, new_power, date, sleep=True):
"""Sending a new power event simulating a change on power sensor"""
_LOGGER.info(
"------- Testu: sending send_temperature_change_event, new_power=%.2f date=%s on %s",
@@ -478,6 +514,7 @@ async def send_presence_change_event(
await asyncio.sleep(0.1)
return ret
async def send_climate_change_event(
entity: BaseThermostat,
new_hvac_mode: HVACMode,
@@ -521,6 +558,7 @@ async def send_climate_change_event(
await asyncio.sleep(0.1)
return ret
async def send_climate_change_event_with_temperature(
entity: BaseThermostat,
new_hvac_mode: HVACMode,
+17 -9
View File
@@ -55,8 +55,12 @@ from custom_components.versatile_thermostat.const import (
CONF_AUTO_REGULATION_NONE,
CONF_AUTO_REGULATION_DTEMP,
CONF_AUTO_REGULATION_PERIOD_MIN,
CONF_INVERSE_SWITCH
CONF_INVERSE_SWITCH,
CONF_AUTO_FAN_HIGH,
CONF_AUTO_FAN_MODE,
PRESET_FROST_PROTECTION,
)
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
CONF_NAME: "TheOverSwitchMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
@@ -103,14 +107,14 @@ MOCK_TH_OVER_SWITCH_TYPE_CONFIG = {
CONF_HEATER: "switch.mock_switch",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False,
CONF_INVERSE_SWITCH: False
CONF_INVERSE_SWITCH: False,
}
MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG = {
CONF_HEATER: "switch.mock_air_conditioner",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: True,
CONF_INVERSE_SWITCH: False
CONF_INVERSE_SWITCH: False,
}
MOCK_TH_OVER_4SWITCH_TYPE_CONFIG = {
@@ -120,7 +124,7 @@ MOCK_TH_OVER_4SWITCH_TYPE_CONFIG = {
CONF_HEATER_4: "switch.mock_4switch3",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_AC_MODE: False,
CONF_INVERSE_SWITCH: False
CONF_INVERSE_SWITCH: False,
}
MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
@@ -133,13 +137,14 @@ MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5,
CONF_AUTO_REGULATION_PERIOD_MIN: 2
CONF_AUTO_REGULATION_PERIOD_MIN: 2,
CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_HIGH,
}
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = {
CONF_CLIMATE: "climate.mock_climate",
CONF_AC_MODE: False,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_NONE
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_NONE,
}
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
@@ -147,16 +152,18 @@ MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
CONF_AC_MODE: True,
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
CONF_AUTO_REGULATION_DTEMP: 0.5,
CONF_AUTO_REGULATION_PERIOD_MIN: 1
CONF_AUTO_REGULATION_PERIOD_MIN: 1,
}
MOCK_PRESETS_CONFIG = {
PRESET_FROST_PROTECTION + "_temp": 7,
PRESET_ECO + "_temp": 16,
PRESET_COMFORT + "_temp": 17,
PRESET_BOOST + "_temp": 18,
}
MOCK_PRESETS_AC_CONFIG = {
PRESET_FROST_PROTECTION + "_temp": 7,
PRESET_ECO + "_temp": 17,
PRESET_COMFORT + "_temp": 19,
PRESET_BOOST + "_temp": 20,
@@ -199,12 +206,13 @@ MOCK_PRESENCE_CONFIG = {
MOCK_PRESENCE_AC_CONFIG = {
CONF_PRESENCE_SENSOR: "person.presence_sensor",
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX + "_temp": 7,
PRESET_ECO + PRESET_AWAY_SUFFIX + "_temp": 16,
PRESET_COMFORT + PRESET_AWAY_SUFFIX + "_temp": 17,
PRESET_BOOST + PRESET_AWAY_SUFFIX + "_temp": 18,
PRESET_ECO + "_ac" + PRESET_AWAY_SUFFIX + "_temp": 27,
PRESET_COMFORT + "_ac" + PRESET_AWAY_SUFFIX + "_temp": 26,
PRESET_BOOST + "_ac" + PRESET_AWAY_SUFFIX + "_temp": 25,
PRESET_COMFORT + "_ac" + PRESET_AWAY_SUFFIX + "_temp": 26,
PRESET_BOOST + "_ac" + PRESET_AWAY_SUFFIX + "_temp": 25,
}
MOCK_ADVANCED_CONFIG = {
+346
View File
@@ -0,0 +1,346 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Test the auto fan mode of a over_climate thermostat """
from unittest.mock import patch, call
from datetime import datetime # , timedelta
from homeassistant.core import HomeAssistant
# from homeassistant.components.climate import HVACAction, HVACMode
from homeassistant.config_entries import ConfigEntryState
# from homeassistant.helpers.entity_component import EntityComponent
# from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry
# from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_climate import (
ThermostatOverClimate,
)
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_auto_fan_mode_turbo(
hass: HomeAssistant, skip_hass_states_is_state, skip_send_event
):
"""Test the init of an over climate thermostat with auto_fan_mode = Turbo which exists"""
fan_modes = ["low", "medium", "high", "boost", "mute", "auto", "turbo"]
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
},
)
fake_underlying_climate = MockClimate(
hass=hass,
unique_id="mockUniqueId",
name="MockClimateName",
fan_modes=fan_modes,
)
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED
entity: ThermostatOverClimate = search_entity(
hass, "climate.theoverclimatemockname", "climate"
)
assert entity
assert isinstance(entity, ThermostatOverClimate)
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate is True
assert entity.fan_modes == fan_modes
assert entity._auto_fan_mode == "auto_fan_turbo"
assert entity._auto_activated_fan_mode == "turbo"
assert entity._auto_deactivated_fan_mode == "mute"
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_auto_fan_mode_not_turbo(
hass: HomeAssistant, skip_hass_states_is_state, skip_send_event
):
"""Test the init of an over climate thermostat with auto_fan_mode = Turbo which doesn't exists"""
fan_modes = ["low", "medium", "high", "boost", "auto"]
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
},
)
fake_underlying_climate = MockClimate(
hass=hass,
unique_id="mockUniqueId",
name="MockClimateName",
fan_modes=fan_modes,
)
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED
entity: ThermostatOverClimate = search_entity(
hass, "climate.theoverclimatemockname", "climate"
)
assert entity
assert isinstance(entity, ThermostatOverClimate)
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate is True
assert entity.fan_modes == fan_modes
assert entity._auto_fan_mode == "auto_fan_turbo"
# Turbo doesn't exists -> fallback to high
assert entity._auto_activated_fan_mode == "high"
# Mute doesn't exists -> fallback to auto
assert entity._auto_deactivated_fan_mode == "auto"
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_climate_auto_fan_mode_turbo_activation(
hass: HomeAssistant, skip_hass_states_is_state, skip_send_event
):
"""Test the init of an over climate thermostat with auto_fan_mode = Turbo which exists"""
fan_modes = ["low", "medium", "high", "boost", "mute", "auto", "turbo"]
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
"eco_ac_temp": 25,
"comfort_ac_temp": 23,
"boost_ac_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
CONF_AC_MODE: True,
},
)
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
fake_underlying_climate = MockClimate(
hass=hass,
unique_id="mockUniqueId",
name="MockClimateName",
fan_modes=fan_modes,
)
# 1. Init fan mode
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED
entity: ThermostatOverClimate = search_entity(
hass, "climate.theoverclimatemockname", "climate"
)
assert entity
assert isinstance(entity, ThermostatOverClimate)
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate is True
assert entity.fan_modes == fan_modes
assert entity.fan_mode is None
assert entity._auto_fan_mode == "auto_fan_turbo"
assert entity._auto_activated_fan_mode == "turbo"
assert entity._auto_deactivated_fan_mode == "mute"
# 2. Turn on and set temperature cold
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
# Force preset mode
await entity.async_set_hvac_mode(HVACMode.HEAT)
assert entity.hvac_mode == HVACMode.HEAT
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode == PRESET_COMFORT
assert entity.target_temperature == 18
# Change the current temperature to 16 which is 2° under
await send_temperature_change_event(entity, 16, now, True)
fake_underlying_climate.set_fan_mode("turbo")
assert mock_send_fan_mode.call_count == 1
mock_send_fan_mode.assert_has_calls([call.set_fan_mode("turbo")])
assert entity.fan_mode == "turbo"
# 3. Set another low temperature
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
fake_underlying_climate.set_fan_mode("turbo")
# Change the current temperature to 17 which is 1° under
await send_temperature_change_event(entity, 15, now, True)
# Nothing is send cause we are already in turbo fan mode
assert mock_send_fan_mode.call_count == 0
assert entity.fan_mode == "turbo"
# 4. Set temperature not so cold
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
# Change the current temperature to 17 which is 1° under
await send_temperature_change_event(entity, 17, now, True)
fake_underlying_climate.set_fan_mode("mute")
assert mock_send_fan_mode.call_count == 1
mock_send_fan_mode.assert_has_calls([call.set_fan_mode("mute")])
assert entity.fan_mode == "mute"
# 5. Set temperature not so cold another time
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
fake_underlying_climate.set_fan_mode("mute")
# Change the current temperature to 17 which is 1° under
await send_temperature_change_event(entity, 17.1, now, True)
assert mock_send_fan_mode.call_count == 0
assert entity.fan_mode == "mute"
# 6. Set temperature very high above the target
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
fake_underlying_climate.set_fan_mode("mute")
# Change the current temperature to 17 which is 1° under
await send_temperature_change_event(entity, 21, now, True)
assert mock_send_fan_mode.call_count == 0
assert entity.fan_mode == "mute"
# 7. In AC mode, set temperature very high under the target
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
await entity.async_set_hvac_mode(HVACMode.COOL)
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
assert entity.target_temperature == 23
assert entity.current_temperature == 21
fake_underlying_climate.set_fan_mode("mute")
# Change the current temperature to 17 which is 1° under
await send_temperature_change_event(entity, 20, now, True)
assert mock_send_fan_mode.call_count == 0
assert entity.fan_mode == "mute"
# 8. In AC mode, set temperature not so high above the target
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
assert entity.target_temperature == 23
await send_temperature_change_event(entity, 24, now, True)
assert entity.current_temperature == 24
fake_underlying_climate.set_fan_mode("mute")
assert mock_send_fan_mode.call_count == 0
assert entity.fan_mode == "mute"
# 8. In AC mode, set temperature high above the target
with patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_fan_mode"
) as mock_send_fan_mode:
assert entity.target_temperature == 23
await send_temperature_change_event(entity, 25.1, now, True)
assert entity.current_temperature == 25.1
fake_underlying_climate.set_fan_mode("turbo")
assert mock_send_fan_mode.call_count == 1
mock_send_fan_mode.assert_has_calls([call.set_fan_mode("turbo")])
assert entity.fan_mode == "turbo"
+2
View File
@@ -76,6 +76,7 @@ async def test_over_climate_regulation(
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -185,6 +186,7 @@ async def test_over_climate_regulation_ac_mode(
assert entity.target_temperature == entity.max_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
+1
View File
@@ -399,6 +399,7 @@ async def test_bug_82(
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
+3
View File
@@ -43,6 +43,7 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
"cycle_min": 5,
"temp_min": 15,
"temp_max": 30,
"frost_temp": 7,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
@@ -72,6 +73,7 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
assert entity.preset_mode is not PRESET_SECURITY
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -444,6 +446,7 @@ async def test_security_over_climate(
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
+5
View File
@@ -61,6 +61,7 @@ async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_s
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -130,6 +131,7 @@ async def test_over_climate_full_start(hass: HomeAssistant, skip_hass_states_is_
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -194,6 +196,7 @@ async def test_over_4switch_full_start(hass: HomeAssistant, skip_hass_states_is_
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
@@ -249,6 +252,7 @@ async def test_over_switch_deactivate_preset(
CONF_CYCLE_MIN: 8,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
"frost_temp": 0,
"eco_temp": 17,
"comfort_temp": 0,
"boost_temp": 19,
@@ -273,6 +277,7 @@ async def test_over_switch_deactivate_preset(
assert entity.preset_modes == [
PRESET_NONE,
# PRESET_FROST_PROTECTION,
PRESET_ECO,
# PRESET_COMFORT,
PRESET_BOOST,
+31 -23
View File
@@ -12,13 +12,18 @@ from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DO
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_switch import ThermostatOverSwitch
from custom_components.versatile_thermostat.thermostat_switch import (
ThermostatOverSwitch,
)
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_is_state): # pylint: disable=unused-argument
async def test_over_switch_ac_full_start(
hass: HomeAssistant, skip_hass_states_is_state
): # pylint: disable=unused-argument
"""Test the normal full start of a thermostat in thermostat_over_switch type"""
entry = MockConfigEntry(
@@ -52,7 +57,7 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
assert isinstance(entity, ThermostatOverSwitch)
assert entity.name == "TheOverSwitchMockName"
assert entity.is_over_climate is False # pylint: disable=protected-access
assert entity.is_over_climate is False # pylint: disable=protected-access
assert entity.ac_mode is True
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
@@ -60,17 +65,18 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
assert entity.target_temperature == entity.max_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
PRESET_ACTIVITY,
]
assert entity.preset_mode is PRESET_NONE
assert entity._security_state is False # pylint: disable=protected-access
assert entity._window_state is None # pylint: disable=protected-access
assert entity._motion_state is None # pylint: disable=protected-access
assert entity._presence_state is None # pylint: disable=protected-access
assert entity._prop_algorithm is not None # pylint: disable=protected-access
assert entity._security_state is False # pylint: disable=protected-access
assert entity._window_state is None # pylint: disable=protected-access
assert entity._motion_state is None # pylint: disable=protected-access
assert entity._presence_state is None # pylint: disable=protected-access
assert entity._prop_algorithm is not None # pylint: disable=protected-access
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
assert mock_send_event.call_count == 2
@@ -91,7 +97,7 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
event_timestamp = now - timedelta(minutes=4)
await send_presence_change_event(entity, True, False, event_timestamp)
assert entity._presence_state == STATE_ON # pylint: disable=protected-access
assert entity._presence_state == STATE_ON # pylint: disable=protected-access
await entity.async_set_hvac_mode(HVACMode.COOL)
assert entity.hvac_mode is HVACMode.COOL
@@ -108,36 +114,39 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
# Unset the presence
event_timestamp = now - timedelta(minutes=3)
await send_presence_change_event(entity, False, True, event_timestamp)
assert entity._presence_state == STATE_OFF # pylint: disable=protected-access
assert entity.target_temperature == 27 # eco_ac_away
assert entity._presence_state == STATE_OFF # pylint: disable=protected-access
assert entity.target_temperature == 27 # eco_ac_away
# Open a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
with patch("homeassistant.helpers.condition.state", return_value=True):
event_timestamp = now - timedelta(minutes=2)
try_condition = await send_window_change_event(entity, True, False, event_timestamp)
try_condition = await send_window_change_event(
entity, True, False, event_timestamp
)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_action is HVACAction.OFF
assert entity.target_temperature == 16 # eco_ac_away
assert entity.target_temperature == 16 # eco_ac_away
# Close a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
with patch("homeassistant.helpers.condition.state", return_value=True):
event_timestamp = now - timedelta(minutes=2)
try_condition = await send_window_change_event(entity, False, True, event_timestamp)
try_condition = await send_window_change_event(
entity, False, True, event_timestamp
)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.COOL
assert (entity.hvac_action is HVACAction.OFF or entity.hvac_action is HVACAction.IDLE)
assert entity.target_temperature == 27 # eco_ac_away
assert (
entity.hvac_action is HVACAction.OFF
or entity.hvac_action is HVACAction.IDLE
)
assert entity.target_temperature == 27 # eco_ac_away
await entity.async_set_hvac_mode(HVACMode.HEAT)
assert entity.hvac_mode is HVACMode.HEAT
@@ -156,4 +165,3 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.preset_mode is PRESET_BOOST
assert entity.target_temperature == 18
+48 -30
View File
@@ -17,8 +17,11 @@ from custom_components.versatile_thermostat.thermostat_valve import ThermostatOv
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_state): # pylint: disable=unused-argument
async def test_over_valve_full_start(
hass: HomeAssistant, skip_hass_states_is_state
): # pylint: disable=unused-argument
"""Test the normal full start of a thermostat in thermostat_over_switch type"""
entry = MockConfigEntry(
@@ -34,6 +37,7 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
PRESET_FROST_PROTECTION + "_temp": 7,
PRESET_ECO + "_temp": 17,
PRESET_COMFORT + "_temp": 19,
PRESET_BOOST + "_temp": 21,
@@ -54,6 +58,7 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
CONF_POWER_SENSOR: "sensor.power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.power_max_sensor",
CONF_PRESENCE_SENSOR: "person.presence_sensor",
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX + "_temp": 7,
PRESET_ECO + PRESET_AWAY_SUFFIX + "_temp": 17.1,
PRESET_COMFORT + PRESET_AWAY_SUFFIX + "_temp": 17.2,
PRESET_BOOST + PRESET_AWAY_SUFFIX + "_temp": 17.3,
@@ -62,7 +67,7 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_DEVICE_POWER: 100,
CONF_AC_MODE: False
CONF_AC_MODE: False,
},
)
@@ -100,17 +105,18 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
assert entity.target_temperature == entity.min_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_FROST_PROTECTION,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
PRESET_ACTIVITY,
]
assert entity.preset_mode is PRESET_NONE
assert entity._security_state is False # pylint: disable=protected-access
assert entity._window_state is None # pylint: disable=protected-access
assert entity._motion_state is None # pylint: disable=protected-access
assert entity._presence_state is None # pylint: disable=protected-access
assert entity._prop_algorithm is not None # pylint: disable=protected-access
assert entity._security_state is False # pylint: disable=protected-access
assert entity._window_state is None # pylint: disable=protected-access
assert entity._motion_state is None # pylint: disable=protected-access
assert entity._presence_state is None # pylint: disable=protected-access
assert entity._prop_algorithm is not None # pylint: disable=protected-access
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
assert mock_send_event.call_count == 2
@@ -147,19 +153,19 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
# set manual target temp
await entity.async_set_temperature(temperature=18)
assert entity.preset_mode == PRESET_NONE # Manual mode
assert entity.preset_mode == PRESET_NONE # Manual mode
assert entity.target_temperature == 18
# Nothing have changed cause we don't have room and external temperature
assert mock_send_event.call_count == 1
# Set temperature and external temperature
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"homeassistant.core.StateMachine.get", return_value=State(entity_id="number.mock_valve", state="90")
"homeassistant.core.StateMachine.get",
return_value=State(entity_id="number.mock_valve", state="90"),
):
# Change temperature
event_timestamp = now - timedelta(minutes=10)
@@ -172,10 +178,20 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
assert entity.hvac_action == HVACAction.HEATING
assert mock_service_call.call_count == 2
mock_service_call.assert_has_calls([
call.async_call('number', 'set_value', {'entity_id': 'number.mock_valve', 'value': 90}),
call.async_call('number', 'set_value', {'entity_id': 'number.mock_valve', 'value': 98})
])
mock_service_call.assert_has_calls(
[
call.async_call(
"number",
"set_value",
{"entity_id": "number.mock_valve", "value": 90},
),
call.async_call(
"number",
"set_value",
{"entity_id": "number.mock_valve", "value": 98},
),
]
)
assert mock_send_event.call_count == 0
@@ -190,10 +206,10 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
# Change presence to on
event_timestamp = now - timedelta(minutes=4)
await send_presence_change_event(entity, True, False, event_timestamp)
assert entity.presence_state == STATE_ON # pylint: disable=protected-access
assert entity.presence_state == STATE_ON # pylint: disable=protected-access
assert entity.preset_mode is PRESET_COMFORT
assert entity.target_temperature == 19
assert entity.valve_open_percent == 100 # Full heating
assert entity.valve_open_percent == 100 # Full heating
assert entity.is_device_active is True
assert entity.hvac_action == HVACAction.HEATING
@@ -211,7 +227,6 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
assert entity.is_device_active is False
assert entity.hvac_action == HVACAction.IDLE
await send_temperature_change_event(entity, 17, datetime.now())
# switch to Eco
await entity.async_set_preset_mode(PRESET_ECO)
@@ -222,38 +237,41 @@ async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_st
# Unset the presence
event_timestamp = now - timedelta(minutes=2)
await send_presence_change_event(entity, False, True, event_timestamp)
assert entity.presence_state == STATE_OFF # pylint: disable=protected-access
assert entity.presence_state == STATE_OFF # pylint: disable=protected-access
assert entity.valve_open_percent == 10
assert entity.target_temperature == 17.1 # eco_away
assert entity.target_temperature == 17.1 # eco_away
assert entity.is_device_active is True
assert entity.hvac_action == HVACAction.HEATING
# Open a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
with patch("homeassistant.helpers.condition.state", return_value=True):
event_timestamp = now - timedelta(minutes=1)
try_condition = await send_window_change_event(entity, True, False, event_timestamp)
try_condition = await send_window_change_event(
entity, True, False, event_timestamp
)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_action is HVACAction.OFF
assert entity.target_temperature == 17.1 # eco
assert entity.target_temperature == 17.1 # eco
assert entity.valve_open_percent == 0
# Close a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
with patch("homeassistant.helpers.condition.state", return_value=True):
event_timestamp = now - timedelta(minutes=0)
try_condition = await send_window_change_event(entity, False, True, event_timestamp)
try_condition = await send_window_change_event(
entity, False, True, event_timestamp
)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.HEAT
assert (entity.hvac_action is HVACAction.OFF or entity.hvac_action is HVACAction.IDLE)
assert entity.target_temperature == 17.1 # eco
assert (
entity.hvac_action is HVACAction.OFF
or entity.hvac_action is HVACAction.IDLE
)
assert entity.target_temperature == 17.1 # eco
assert entity.valve_open_percent == 10