Compare commits
21 Commits
5.0.0-alph
...
5.2.3.beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8bb465b43 | ||
|
|
d7ec6770c4 | ||
|
|
51428aa875 | ||
|
|
6ea6fe8542 | ||
|
|
a18d10fa3f | ||
|
|
7d4ee40b4d | ||
|
|
1aaf9c8c8e | ||
|
|
ae93a8b97c | ||
|
|
cbe98ae20c | ||
|
|
bfcc854c3e | ||
|
|
683aa050f3 | ||
|
|
7476e7fa64 | ||
|
|
c222feda1a | ||
|
|
d05df021ab | ||
|
|
27a267139f | ||
|
|
707f40d406 | ||
|
|
a01f5770d9 | ||
|
|
04d0b28f1d | ||
|
|
30c3418f1b | ||
|
|
efb8ce257d | ||
|
|
8f934a3298 |
@@ -1,5 +1,8 @@
|
||||
default_config:
|
||||
|
||||
# ffmeg
|
||||
ffmpeg:
|
||||
|
||||
logger:
|
||||
default: info
|
||||
logs:
|
||||
@@ -59,8 +62,8 @@ input_number:
|
||||
unit_of_measurement: kW
|
||||
fake_valve1:
|
||||
name: The valve 1
|
||||
min: 0
|
||||
max: 100
|
||||
min: 10
|
||||
max: 90
|
||||
icon: mdi:pipe-valve
|
||||
unit_of_measurement: percentage
|
||||
|
||||
|
||||
@@ -32,10 +32,6 @@
|
||||
"vscode.markdown-math",
|
||||
"yzhang.markdown-all-in-one"
|
||||
],
|
||||
// "mounts": [
|
||||
// "source=${localWorkspaceFolder}/.devcontainer/configuration.yaml,target=${localWorkspaceFolder}/config/www/community/,type=bind,consistency=cached",
|
||||
// "source=${localWorkspaceFolder}/custom_components,target=/home/vscode/core/config/custom_components,type=bind,consistency=cached"
|
||||
// ],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"editor.tabSize": 4,
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/issue.md
vendored
4
.github/ISSUE_TEMPLATE/issue.md
vendored
@@ -4,6 +4,8 @@ about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!-- This template will allow the maintainer to be efficient and post the more accurante response as possible. There is many types / modes / configuration possible, so the analysis can be very tricky. If don't follow this template, your issue could be rejected without any message. Please help me to help you. -->
|
||||
|
||||
<!-- Before you open a new issue, search through the existing issues to see if others have had the same problem.
|
||||
|
||||
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).
|
||||
@@ -13,7 +15,7 @@ Check also in the [Troubleshooting](#troubleshooting) paragrah of the README if
|
||||
Issues not containing the minimum requirements will be closed:
|
||||
|
||||
- Issues without a description (using the header is not good enough) will be closed.
|
||||
- Issues without configuration will be closed
|
||||
- Issues that don't follow this template could be closed
|
||||
|
||||
-->
|
||||
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -14,7 +14,8 @@
|
||||
"python.testing.pytestEnabled": true,
|
||||
"python.analysis.extraPaths": [
|
||||
// "/home/vscode/core",
|
||||
"/workspaces/versatile_thermostat/custom_components/versatile_thermostat"
|
||||
"/workspaces/versatile_thermostat/custom_components/versatile_thermostat",
|
||||
"/home/vscode/.local/lib/python3.11/site-packages/homeassistant"
|
||||
],
|
||||
"python.formatting.provider": "none"
|
||||
}
|
||||
224
README-fr.md
224
README-fr.md
@@ -8,7 +8,7 @@
|
||||
|
||||
>  Cette intégration de thermostat vise à simplifier considérablement vos automatisations autour de la gestion du chauffage. Parce que tous les événements autour du chauffage classiques sont gérés nativement par le thermostat (personne à la maison ?, activité détectée dans une pièce ?, fenêtre ouverte ?, délestage de courant ?), vous n'avez pas à vous encombrer de scripts et d'automatismes compliqués pour gérer vos climats. ;-).
|
||||
|
||||
- [Changements majeurs dans la version 4.0.0](#changements-majeurs-dans-la-version-400)
|
||||
- [Changements majeurs dans la version 5.0](#changements-majeurs-dans-la-version-50)
|
||||
- [Merci pour la bière buymecoffee](#merci-pour-la-bière-buymecoffee)
|
||||
- [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser)
|
||||
- [Incompatibilités](#incompatibilités)
|
||||
@@ -17,6 +17,7 @@
|
||||
- [HACS installation (recommendé)](#hacs-installation-recommendé)
|
||||
- [Installation manuelle](#installation-manuelle)
|
||||
- [Configuration](#configuration)
|
||||
- [Création d'un nouveau Versatile Thermostat](#création-dun-nouveau-versatile-thermostat)
|
||||
- [Choix des attributs de base](#choix-des-attributs-de-base)
|
||||
- [Sélectionnez des entités pilotées](#sélectionnez-des-entités-pilotées)
|
||||
- [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
|
||||
@@ -34,6 +35,7 @@
|
||||
- [Configurer la gestion de la puissance](#configurer-la-gestion-de-la-puissance)
|
||||
- [Configurer la présence ou l'occupation](#configurer-la-présence-ou-loccupation)
|
||||
- [Configuration avancée](#configuration-avancée)
|
||||
- [Le contrôle centralisé](#le-contrôle-centralisé)
|
||||
- [Synthèse des paramètres](#synthèse-des-paramètres)
|
||||
- [Exemples de réglage](#exemples-de-réglage)
|
||||
- [Chauffage électrique](#chauffage-électrique)
|
||||
@@ -63,6 +65,9 @@
|
||||
- [Utilisation d'un Heatzy](#utilisation-dun-heatzy)
|
||||
- [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)
|
||||
- [Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne](#le-radiateur-chauffe-alors-que-la-température-de-consigne-est-dépassée-ou-ne-chauffe-pas-alors-que-la-température-de-la-pièce-est-bien-en-dessous-de-la-consigne)
|
||||
- [Type `over_switch` ou `over_valve`](#type-over_switch-ou-over_valve)
|
||||
- [Type `over_climate`](#type-over_climate)
|
||||
- [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é-)
|
||||
@@ -74,13 +79,16 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et est une
|
||||
|
||||
|
||||
>  _*Nouveautés*_
|
||||
> * **Release 5.2** : Ajout d'un `central_mode` permettant de piloter tous les VTherms de façon centralisée [#158](https://github.com/jmcollin78/versatile_thermostat/issues/158).
|
||||
> * **Release 5.1** : Limitation des valeurs envoyées aux valves et au température envoyées au climate sous-jacent.
|
||||
> * **Release 5.0** : Ajout d'une configuration centrale permettant de mettre en commun les attributs qui peuvent l'être [#239](https://github.com/jmcollin78/versatile_thermostat/issues/239).
|
||||
> * **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)
|
||||
<details>
|
||||
<summary>Autres versions</summary>
|
||||
|
||||
> * **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)
|
||||
> * **Release 3.8**: Ajout d'une **fonction d'auto-régulation** pour les thermostats `over climate` dont la régulation est faite par le climate sous-jacent. Cf. [L'auto-régulation](#lauto-régulation) et [#129](https://github.com/jmcollin78/versatile_thermostat/issues/129). Ajout de la **possibilité d'inverser la commande** pour un thermostat `over switch` pour adresser les installations avec fil pilote et diode [#124](https://github.com/jmcollin78/versatile_thermostat/issues/124).
|
||||
> * **Release 3.7**: Ajout du type de **Versatile Thermostat `over valve`** pour piloter une vanne TRV directement ou tout autre équipement type gradateur pour le chauffage. La régulation se fait alors directement en agissant sur le pourcentage d'ouverture de l'entité sous-jacente : 0 la vanne est coupée, 100 : la vanne est ouverte à fond. Cf. [#131](https://github.com/jmcollin78/versatile_thermostat/issues/131). Ajout d'une fonction permettant le bypass de la détection d'ouverture [#138](https://github.com/jmcollin78/versatile_thermostat/issues/138). Ajout de la langue Slovaque
|
||||
> * **Release 3.6**: Ajout du paramètre `motion_off_delay` pour améliorer la gestion de des mouvements [#116](https://github.com/jmcollin78/versatile_thermostat/issues/116), [#128](https://github.com/jmcollin78/versatile_thermostat/issues/128). Ajout du mode AC (air conditionné) pour un VTherm over switch. Préparation du projet Github pour faciliter les contributions [#127](https://github.com/jmcollin78/versatile_thermostat/issues/127)
|
||||
@@ -95,12 +103,25 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et est une
|
||||
> * **release majeure 2.0** : ajout du thermostat "over climate" permettant de transformer n'importe quel thermostat en Versatile Thermostat et lui ajouter toutes les fonctions de ce dernier.
|
||||
</details>
|
||||
|
||||
# Changements majeurs dans la version 4.0.0
|
||||
1. La puissance de l'appareil doit maintenant être la puissance totale de tous les appareils controlée par le VTherm. Cela permet d'avoir des équipements hétérogènes de puissance différente. Dans le cas de plusieurs appareils contrôlés par un seul VTherm, vous devrez éditer et changer la valeur `device_power`. Vous devez configurer la puissance totale de tous les appareils.
|
||||
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.
|
||||
# Changements majeurs dans la version 5.0
|
||||

|
||||
|
||||
Vous pouvez maintenant définir une configuration centrale qui va vous permettre de mettre en commun sur tous vos VTherms (ou seulement une partie), certains attributs. Pour utiliser cette possibilité, vous devez :
|
||||
1. Créer un VTherm de type "Configuration Centrale",
|
||||
2. Saisir les attributs de cette configuration centrale
|
||||
|
||||
Pour l'utiliser ensuite dans les autres VTherms, vous devez les reconfigurer et à chaque fois que c'est possible cocher la case "Utiliser la configuration centrale". Cette case à cocher apparait dans tous les groupes d'attributs qui peuvent avoir recours à la configuration centrale : attributs principaux, TPI, ouvertures, mouvement, puissance, présence et paramètres avancés.
|
||||
|
||||
Les attributs configurable dans la configuration centrale est listée ici : [Synthèse des paramètres](#synthèse-des-paramètres).
|
||||
|
||||
Lors d'un changement sur la configuration centrale, tous les VTherms seront rechargés pour tenir compte de ces changements.
|
||||
|
||||
En conséquence toute la phase de paramètrage d'un VTherm a été profondemment modifiée pour pouvoir utiliser la configuration centrale ou surcharger les valeurs de la configuration centrale par des valeurs propre au VTherm en cours de configuration.
|
||||
|
||||
**Note :** les copies d'écran de la configuration d'un VTherm n'ont pas été mises à jour.
|
||||
|
||||
# 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, @abyssmal, @capinfo26, @Helge, @MattG 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, @capinfo26, @Helge, @MattG @Mexx62, @Someone, @Lajull pour les bières. Ca fait très plaisir et ça m'encourage à continuer !
|
||||
|
||||
|
||||
# Quand l'utiliser et ne pas l'utiliser
|
||||
@@ -136,7 +157,8 @@ Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivant
|
||||
- Ajouter une **gestion de délestage** ou une régulation pour ne pas dépasser une puissance totale définie. Lorsque la puissance maximale est dépassée, un préréglage caché de « puissance » est défini sur l'entité climatique. Lorsque la puissance passe en dessous du maximum, le préréglage précédent est restauré.
|
||||
- La **gestion de la présence à domicile**. Cette fonctionnalité vous permet de modifier dynamiquement la température du préréglage en tenant compte d'un capteur de présence de votre maison.
|
||||
- Des **services pour interagir avec le thermostat** à partir d'autres intégrations : vous pouvez forcer la présence / la non-présence à l'aide d'un service, et vous pouvez modifier dynamiquement la température des préréglages et changer les paramètres de sécurité.
|
||||
- Ajouter des capteurs pour voir les états internes du thermostat.
|
||||
- Ajouter des capteurs pour voir les états internes du thermostat,
|
||||
- Contrôle centralisé de tous les Versatile Thermostat pour les stopper tous, les passer tous en hors-gel, les forcer en mode Chauffage (l'hiver), les forcer en mode Climatisation (l'été).
|
||||
|
||||
# Comment installer cet incroyable Thermostat Versatile ?
|
||||
|
||||
@@ -161,8 +183,17 @@ Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivant
|
||||
|
||||
# Configuration
|
||||
|
||||
Note: aucune configuration dans configuration.yaml n'est nécessaire car toute la configuration est effectuée via l'interface graphique standard lors de l'ajout de l'intégration.
|
||||
-- VTherm = Versatile Thermostat dans la suite de ce document --
|
||||
|
||||
>  _*Notes*_
|
||||
>
|
||||
> Trois façons de configurer les VTherms sont disponibles :
|
||||
> 1. Chaque Versatile Thermostat est entièrement configurée de manière indépendante. Choisissez cette option si vous ne souhaitez avoir aucune configuration ou gestion centrale.
|
||||
> 2. Certains aspects sont configurés de manière centralisée. Cela permet par ex. définir la température min/max, la détection de fenêtre ouverte,… au niveau d'une instance centrale et unique. Pour chaque VTherm que vous configurez, vous pouvez alors choisir d'utiliser la configuration centrale ou de la remplacer par des paramètres personnalisés.
|
||||
> 3. En plus de cette configuration centralisée, tous les VTherm peuvent être contrôlées par une seule entité de type `select`. Cette fonction est nommé `central_mode`. Cela permet de stopper / démarrer / mettre en hors gel / etc tous les VTherms en une seule fois. Pour chaque VTherm, l'utilisateur indique si il est concerné par ce `central_mode`.
|
||||
|
||||
|
||||
## Création d'un nouveau Versatile Thermostat
|
||||
Cliquez sur le bouton Ajouter une intégration dans la page d'intégration
|
||||
|
||||

|
||||
@@ -173,7 +204,9 @@ Suivez ensuite les étapes de configuration comme suit :
|
||||
|
||||
## Choix des attributs de base
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
Donnez les principaux attributs obligatoires :
|
||||
1. un nom (sera le nom de l'intégration et aussi le nom de l'entité climate)
|
||||
@@ -183,7 +216,8 @@ Donnez les principaux attributs obligatoires :
|
||||
6. une durée de cycle en minutes. A chaque cycle, le radiateur s'allumera puis s'éteindra pendant une durée calculée afin d'atteindre la température ciblée (voir [preset](#configure-the-preset-temperature) ci-dessous). En mode ```over_climate```, le cycle ne sert qu'à faire des controles de base mais ne régule pas directement la température. C'est le ```climate``` sous-jacent qui le fait,
|
||||
7. les températures minimales et maximales du thermostat,
|
||||
8. une puissance de l'équipement ce qui va activer les capteurs de puissance et énergie consommée par l'appareil,
|
||||
9. la liste des fonctionnalités qui seront utilisées pour ce thermostat. En fonction de vos choix, les écrans de configuration suivants s'afficheront ou pas.
|
||||
9. la possibilité de controler le thermostat de façon centralisée. Cf [controle centralisé](#le-contrôle-centralisé),
|
||||
10. la liste des fonctionnalités qui seront utilisées pour ce thermostat. En fonction de vos choix, les écrans de configuration suivants s'afficheront ou pas.
|
||||
|
||||
>  _*Notes*_
|
||||
> 1. avec les types ```over_switch``` et ```over_valve```, les calculs sont effectués à chaque cycle. Donc en cas de changement de conditions, il faudra attendre le prochain cycle pour voir un changement. Pour cette raison, le cycle ne doit pas être trop long. **5 min est une bonne valeur**,
|
||||
@@ -445,7 +479,7 @@ Si vous avez choisi la fonctionnalité ```Avec détection de la puissance```, cl
|
||||
|
||||

|
||||
|
||||
Cette fonction vous permet de réguler la consommation électrique de vos radiateurs. Connue sous le nom de délestage, cette fonction vous permet de limiter la consommation électrique de votre appareil de chauffage si des conditions de surpuissance sont détectées. Donnez un **capteur à la consommation électrique actuelle de votre maison**, un **capteur à la puissance max** qu'il ne faut pas dépasser, la **consommation électrique de votre chauffage** (en étape 1 de la configuration) et l'algorithme ne démarrera pas un radiateur si la puissance maximale sera dépassée après le démarrage du radiateur.
|
||||
Cette fonction vous permet de réguler la consommation électrique de vos radiateurs. Connue sous le nom de délestage, cette fonction vous permet de limiter la consommation électrique de votre appareil de chauffage si des conditions de surpuissance sont détectées. Donnez un **capteur à la consommation électrique actuelle de votre maison**, un **capteur à la puissance max** qu'il ne faut pas dépasser, la **consommation électrique totale des équipements du VTherm** (en étape 1 de la configuration) et l'algorithme ne démarrera pas un radiateur si la puissance maximale sera dépassée après le démarrage du radiateur.
|
||||
|
||||
Notez que toutes les valeurs de puissance doivent avoir les mêmes unités (kW ou W par exemple).
|
||||
Cela vous permet de modifier la puissance maximale au fil du temps à l'aide d'un planificateur ou de ce que vous voulez.
|
||||
@@ -501,71 +535,90 @@ Voir [exemple de réglages](#examples-tuning) pour avoir des exemples de réglag
|
||||
> 4. Pour un usage naturel, le ``security_default_on_percent`` doit être inférieur à ``security_min_on_percent``,
|
||||
> 5. Les thermostats de type ``thermostat_over_climate`` ne sont pas concernés par le mode security.
|
||||
|
||||
## Le contrôle centralisé
|
||||
Depuis la release 5.2, si vous avez défini une configuration centralisée, vous avez une nouvelle entité nommée `select.central_mode` qui permet de piloter tous les VTherms avec une seule action. Pour qu'un VTherm soit contrôlable de façon centralisée, il faut que son attribut de configuration nommé `use_central_mode` soit vrai.
|
||||
|
||||
Cette entité se présente sous la forme d'une liste de choix qui contient les choix suivants :
|
||||
1. `Auto` : le mode 'normal' dans lequel chaque VTherm se comporte comme dans les versions précédentes,
|
||||
2. `Stooped` : tous les VTherms sont mis à l'arrêt (`hvac_off`),
|
||||
3. `Heat only` : tous les VTherms sont mis en mode chauffage lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
|
||||
3. `Cool only` : tous les VTherms sont mis en mode climatisation lorsque ce mode est supporté par le VTherm, sinon il est stoppé,
|
||||
4. `Frost protection` : tous les VTherms sont mis en preset hors-gel lorsque ce preset est supporté par le VTherm, sinon il est stoppé.
|
||||
|
||||
Il est donc possible de contrôler tous les VTherms (que ceux que l'on désigne explicitement) avec un seul contrôle.
|
||||
Exemple de rendu :
|
||||
|
||||

|
||||
|
||||
## 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" | "configuration centrale" |
|
||||
| ----------------------------------------- | --------------------------------------------------------------------------------- | ------------- | ------------------- | ------------ | ------------------------ |
|
||||
| ``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 | X |
|
||||
| ``cycle_min`` | Durée du cycle (minutes) | X | X | X | - |
|
||||
| ``temp_min`` | Température minimale permise | X | X | X | X |
|
||||
| ``temp_max`` | Température maximale permise | X | X | X | X |
|
||||
| ``device_power`` | Puissance de l'équipement | X | X | X | - |
|
||||
| ``use_central_mode`` | Autorisation du contrôle centralisé | 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 | X |
|
||||
| ``tpi_coef_ext`` | Coefficient à utiliser pour le delta de température externe | X | - | X | X |
|
||||
| ``frost_tp`` | Température en preset Hors-gel | X | X | X | X |
|
||||
| ``eco_temp`` | Température en preset Eco | X | X | X | X |
|
||||
| ``comfort_temp`` | Température en preset Confort | X | X | X | X |
|
||||
| ``boost_temp`` | Température en preset Boost | X | X | X | X |
|
||||
| ``eco_ac_temp`` | Température en preset Eco en mode AC | X | X | X | X |
|
||||
| ``comfort_ac_temp`` | Température en preset Confort en mode AC | X | X | X | X |
|
||||
| ``boost_ac_temp`` | Température en preset Boost en mode AC | X | 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 | X |
|
||||
| ``window_auto_open_threshold`` | Seuil haut de chute de température pour la détection automatique (en °/min) | X | 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 | X |
|
||||
| ``window_auto_max_duration`` | Durée maximum d'une extinction automatique (en min) | X | 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 | X |
|
||||
| ``motion_preset`` | Preset à utiliser si mouvement détecté | X | X | X | X |
|
||||
| ``no_motion_preset`` | Preset à utiliser si pas de mouvement détecté | X | X | X | X |
|
||||
| ``power_sensor_entity_id`` | Capteur de puissance totale (entity id) | X | X | X | X |
|
||||
| ``max_power_sensor_entity_id`` | Capteur de puissance Max (entity id) | X | X | X | X |
|
||||
| ``power_temp`` | Température si délestaqe | X | X | X | X |
|
||||
| ``presence_sensor_entity_id`` | Capteur de présence entity id (true si quelqu'un est présent) | X | X | X | - |
|
||||
| ``frost_ay_temp`` | Température en preset Hors-gel en cas d'absence | X | X | X | X |
|
||||
| ``eco_away_temp`` | Température en preset Eco en cas d'absence | X | X | X | X |
|
||||
| ``comfort_away_temp`` | Température en preset Comfort en cas d'absence | X | X | X | X |
|
||||
| ``boost_away_temp`` | Température en preset Boost en cas d'absence | X | X | X | X |
|
||||
| ``eco_ac_away_temp`` | Température en preset Eco en cas d'absence en mode AC | X | X | X | X |
|
||||
| ``comfort_ac_away_temp`` | Température en preset Comfort en cas d'absence en mode AC | X | X | X | X |
|
||||
| ``boost_ac_away_temp`` | Température en preset Boost en cas d'absence en mode AC | X | X | X | X |
|
||||
| ``minimal_activation_delay`` | Délai minimal d'activation | X | - | - | X |
|
||||
| ``security_delay_min`` | Délai maximal entre 2 mesures de températures | X | - | X | X |
|
||||
| ``security_min_on_percent`` | Pourcentage minimal de puissance pour passer en mode sécurité | X | - | 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 | - | - | - |
|
||||
| ``auto_fan_mode` | Mode de ventilation automatique | - | X | - | - |
|
||||
|
||||
# Exemples de réglage
|
||||
|
||||
@@ -599,11 +652,13 @@ Versatile Thermostat vous permet d'être notifié lorsqu'un évènement de ce ty
|
||||
|
||||
## Mes presets
|
||||
Ceci est juste un exemple de la façon dont j'utilise le préréglage. A vous de vous adapter à votre configuration mais cela peut être utile pour comprendre son fonctionnement.
|
||||
``Hors gel`` : 10 °C
|
||||
``Éco`` : 17 °C
|
||||
``Confort`` : 19 °C
|
||||
``Boost`` : 20 °C
|
||||
|
||||
Lorsque la présence est désactivée :
|
||||
``Hors gel`` : 10 °C
|
||||
``Éco`` : 16,5 °C
|
||||
``Confort`` : 17 °C
|
||||
``Boost`` : 18 °C
|
||||
@@ -816,6 +871,8 @@ Les attributs personnalisés sont les suivants :
|
||||
| ``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) |
|
||||
| ``is_controlled_by_central_mode`` | True si le VTherm peut être controlé de façon centrale |
|
||||
| ``last_central_mode`` | Le dernier mode central utilisé (None si le VTherm n'est pas controlé en central) |
|
||||
|
||||
# Quelques résultats
|
||||
|
||||
@@ -989,6 +1046,10 @@ Remplacez les valeurs entre [[ ]] par les votres.
|
||||
step: day
|
||||
```
|
||||
|
||||
Exemple de courbes obtenues avec Plotly :
|
||||
|
||||

|
||||
|
||||
## Et toujours de mieux en mieux avec l'AappDaemon NOTIFIER pour notifier les évènements
|
||||
Cette automatisation utilise l'excellente App Daemon nommée NOTIFIER développée par Horizon Domotique que vous trouverez en démonstration [ici](https://www.youtube.com/watch?v=chJylIK0ASo&ab_channel=HorizonDomotique) et le code est [ici](https://github.com/jlpouffier/home-assistant-config/blob/master/appdaemon/apps/notifier.py). Elle permet de notifier les utilisateurs du logement lorsqu'un des évènements touchant à la sécurité survient sur un des Versatile Thermostats.
|
||||
|
||||
@@ -1137,6 +1198,19 @@ Exemple :
|
||||
En mode `over_switch` si plusieurs radiateurs sont configurés pour un même VTherm, l'alllumage va se faire de façon séquentiel pour lisser au plus possible les pics de consommation.
|
||||
Cela est tout à fait normal et voulu. C'est décrit ici : [Pour un thermostat de type ```thermostat_over_switch```](#pour-un-thermostat-de-type-thermostat_over_switch)
|
||||
|
||||
## Le radiateur chauffe alors que la température de consigne est dépassée ou ne chauffe pas alors que la température de la pièce est bien en-dessous de la consigne
|
||||
|
||||
### Type `over_switch` ou `over_valve`
|
||||
Avec un VTherm de type `over_switch` ou `over_valve`, ce défaut montre juste que les paramètres de l'algorithme TPI sont mal réglés. Voir [Algorithme TPI](#algorithme-tpi) pour optimiser les réglages.
|
||||
|
||||
### Type `over_climate`
|
||||
Avec un VTherm de type `over_climate`, la régulation est faite par le `climate` sous-jacent directement et VTherm se contente de lui transmettre les consignes. Donc si le radiateur chauffe alors que la température de consigne est dépassée, c'est certainement que sa mesure de température interne est biaisée. Ca arrive très souvent avec les TRV et les clims réversibles qui ont un capteur de température interne, soit trop près de l'élément de chauffe (donc trop froid l'hiver).
|
||||
|
||||
Exemple de discussion autour de ces sujets: [#316](https://github.com/jmcollin78/versatile_thermostat/issues/316), [#312](https://github.com/jmcollin78/versatile_thermostat/discussions/312), [#278](https://github.com/jmcollin78/versatile_thermostat/discussions/278)
|
||||
|
||||
Pour s'en sortir, VTherm est équipé d'une fonction nommée auto-régulation qui permet d'adapter la consigne envoyée au sous-jacent jusqu'à ce que la consigne soit respectée. Cette fonction permet de compenser le biais de mesure des thermomètres internes. Si le biais est important la régulation doit être importante. Voir [L'auto-régulation](#lauto-régulation) pour configurer l'auto-régulation.
|
||||
|
||||
|
||||
## Régler les paramètres de détection d'ouverture de fenêtre en mode auto
|
||||
|
||||
Si vous n'arrivez pas à régler la fonction de détection des ouvertures en mode auto (cf. [auto](#le-mode-auto)), vous pouvez essayer de modifier les paramètres de l'algorithme de lissage de la température.
|
||||
|
||||
297
README.md
297
README.md
@@ -8,7 +8,7 @@
|
||||
|
||||
>  This thermostat integration aims to drastically simplify your automations around climate management. Because all classical events in climate are natively handled by the thermostat (nobody at home ?, activity detected in a room ?, window open ?, power shedding ?), you don't have to build over complicated scripts and automations to manage your climates ;-).
|
||||
|
||||
- [Breaking changes in 4.0.0](#breaking-changes-in-400)
|
||||
- [Major changes in version 5.0](#major-changes-in-version-50)
|
||||
- [Thanks for the beer buymecoffee](#thanks-for-the-beer-buymecoffee)
|
||||
- [When to use / not use](#when-to-use--not-use)
|
||||
- [Incompatibilities](#incompatibilities)
|
||||
@@ -17,6 +17,7 @@
|
||||
- [HACS installation (recommended)](#hacs-installation-recommended)
|
||||
- [Manual installation](#manual-installation)
|
||||
- [Configuration](#configuration)
|
||||
- [Creation of a new Versatile Thermostat](#creation-of-a-new-versatile-thermostat)
|
||||
- [Minimal configuration update](#minimal-configuration-update)
|
||||
- [Select the driven entity](#select-the-driven-entity)
|
||||
- [For a ```thermostat_over_switch``` type thermostat](#for-a-thermostat_over_switch-type-thermostat)
|
||||
@@ -34,6 +35,7 @@
|
||||
- [Configure the power management](#configure-the-power-management)
|
||||
- [Configure presence or occupancy](#configure-presence-or-occupancy)
|
||||
- [Advanced configuration](#advanced-configuration)
|
||||
- [Centralized control](#centralized-control)
|
||||
- [Parameters synthesis](#parameters-synthesis)
|
||||
- [Examples tuning](#examples-tuning)
|
||||
- [Electrical heater](#electrical-heater)
|
||||
@@ -47,7 +49,7 @@
|
||||
- [Services](#services)
|
||||
- [Force the presence / occupancy](#force-the-presence--occupancy)
|
||||
- [Change the temperature of presets](#change-the-temperature-of-presets)
|
||||
- [Change security settings](#change-security-settings)
|
||||
- [Change safety settings](#change-safety-settings)
|
||||
- [ByPass Window Check](#bypass-window-check)
|
||||
- [Events](#events)
|
||||
- [Custom attributes](#custom-attributes)
|
||||
@@ -63,9 +65,12 @@
|
||||
- [Using a Heatzy](#using-a-heatzy)
|
||||
- [Using a Heatsink with a Pilot Wire](#using-a-heatsink-with-a-pilot-wire)
|
||||
- [Only the first radiator heats](#only-the-first-radiator-heats)
|
||||
- [The radiator heats up even though the setpoint temperature is exceeded or does not heat up even though the room temperature is well below the setpoint](#the-radiator-heats-up-even-though-the-setpoint-temperature-is-exceeded-or-does-not-heat-up-even-though-the-room-temperature-is-well-below-the-setpoint)
|
||||
- [Type `over_switch` or `over_valve`](#type-over_switch-or-over_valve)
|
||||
- [Type `over_climate`](#type-over_climate)
|
||||
- [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 to detect safety mode?](#how-to-detect-safety-mode)
|
||||
- [How can I be notified when this happens?](#how-can-i-be-notified-when-this-happens)
|
||||
- [How to repair?](#how-to-repair)
|
||||
- [Using a group of people as a presence sensor](#using-a-group-of-people-as-a-presence-sensor)
|
||||
@@ -74,15 +79,18 @@
|
||||
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.
|
||||
|
||||
> _*News*_
|
||||
> * **Release 5.2**: Added a `central_mode` allowing all VTherms to be controlled centrally [#158](https://github.com/jmcollin78/versatile_thermostat/issues/158).
|
||||
> * **Release 5.1**: Limitation of the values sent to the valves and the temperature sent to the underlying climate.
|
||||
> * **Release 5.0**: Added a central configuration allowing the sharing of attributes that can be shared [#239](https://github.com/jmcollin78/versatile_thermostat/issues/239).
|
||||
> * **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.
|
||||
<details>
|
||||
<summary>Others releases</summary>
|
||||
|
||||
> * **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).
|
||||
> * **Release 3.7**: Addition of the **Versatile Thermostat type `over valve`** to control a TRV valve directly or any other dimmer type equipment for heating. Regulation is then done directly by acting on the opening percentage of the underlying entity: 0 the valve is cut off, 100: the valve is fully opened. See [#131](https://github.com/jmcollin78/versatile_thermostat/issues/131). Added a function allowing the bypass of opening detection [#138](https://github.com/jmcollin78/versatile_thermostat/issues/138). Added Slovak language
|
||||
<details>
|
||||
<summary>Others releases</summary>
|
||||
|
||||
> * **Release 3.6**: Added the `motion_off_delay` parameter to improve motion management [#116](https://github.com/jmcollin78/versatile_thermostat/issues/116), [#128](https://github.com/jmcollin78/versatile_thermostat/issues/128). Added AC (air conditioning) mode for a VTherm over switch. Preparing the Github project to facilitate contributions [#127](https://github.com/jmcollin78/versatile_thermostat/issues/127)
|
||||
> * **Release 3.5**: Multiple thermostats when using "thermostat over another thermostat" mode [#113](https://github.com/jmcollin78/versatile_thermostat/issues/113)
|
||||
> * **Release 3.4**: bug fixes and expose preset temperatures for AC mode [#103](https://github.com/jmcollin78/versatile_thermostat/issues/103)
|
||||
@@ -95,12 +103,25 @@ This custom component for Home Assistant is an upgrade and is a complete rewrite
|
||||
> * **major release 2.0**: addition of the "over climate" thermostat allowing you to transform any thermostat into a Versatile Thermostat and add all the functions of the latter.
|
||||
</details>
|
||||
|
||||
# Breaking changes in 4.0.0
|
||||
1. The power of the device should now be the total power of all controler devices by the VTherm. This allow to have eterogeneous equipment with different power. In case of multi-devices controlled by a single VTherm you will have to edit and change the `device_power` value. Set the total power of all devices.
|
||||
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.
|
||||
# Major changes in version 5.0
|
||||

|
||||
|
||||
You can now define a central configuration which will allow you to share certain attributes on all your VTherms (or only part of them). To use this possibility, you must:
|
||||
1. Create a VTherm of type “Central Configuration”,
|
||||
2. Enter the attributes of this central configuration
|
||||
|
||||
To then use it in the other VTherms, you must reconfigure them and whenever possible check the "Use central configuration" box. This check box appears in all attribute groups that can use central configuration: main attributes, TPI, openings, movement, power, presence and advanced parameters.
|
||||
|
||||
The configurable attributes in the central configuration are listed here: [Parameter summary](#parameter-synthesis).
|
||||
|
||||
When changing the central configuration, all VTherms will be reloaded to take these changes into account.
|
||||
|
||||
Consequently, the entire configuration phase of a VTherm has been profoundly modified to be able to use the central configuration or overload the values of the central configuration with values specific to the VTherm being configured.
|
||||
|
||||
**Note:** the VTherm configuration screenshots have not been updated.
|
||||
|
||||
# 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, @abyssmal, @capinfo26, @Helge, @MattG 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, @capinfo26, @Helge, @MattG, @MattG, @Mexx62, @Someone, @Lajull 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:
|
||||
@@ -135,8 +156,9 @@ This component named __Versatile thermostat__ manage the following use cases :
|
||||
- Use a **TPI (Time Proportional Interval) algorithm** thank's to [[Argonaute](https://forum.hacf.fr/u/argonaute/summary)] algorithm ,
|
||||
- Add **power shedding management** or regulation to avoid exceeding a defined total power. When max power is exceeded, a hidden 'power' preset is set on the climate entity. When power goes below the max, the previous preset is restored.
|
||||
- Add **home presence management**. This feature allows you to dynamically change the temperature of preset considering a occupancy sensor of your home.
|
||||
- Add **services to interact with the thermostat** from others integration: you can force the presence / un-presence using a service, and you can dynamically change the temperature of the presets and change dynamically the security parameters.
|
||||
- Add sensors to see the internal states of the thermostat
|
||||
- Add **services to interact with the thermostat** from others integration: you can force the presence / un-presence using a service, and you can dynamically change the temperature of the presets and change dynamically the safety parameters.
|
||||
- Add sensors to see the internal states of the thermostat,
|
||||
- Centralized control of all Versatile Thermostats to stop them all, switch them all to frost protection, force them into Heating mode (winter), force them into Cooling mode (summer).
|
||||
|
||||
# How to install this incredible Versatile Thermostat ?
|
||||
|
||||
@@ -161,8 +183,17 @@ This component named __Versatile thermostat__ manage the following use cases :
|
||||
|
||||
# Configuration
|
||||
|
||||
Note: no configuration in configuration.yaml is needed because all configuration is done through the standard GUI when adding the integration.
|
||||
-- VTherm = Versatile Thermostat in the remainder of this document --
|
||||
|
||||
>  _*Notes*_
|
||||
>
|
||||
> Three ways to configure VTherms are available:
|
||||
> 1. Each Versatile Thermostat is completely independently configured. Choose this option if you do not want to have any central configuration or management.
|
||||
> 2. Some aspects are configured centrally. This allows e.g. define the min/max temperature, open window detection, etc. at the level of a single central instance. For each VTherm you configure, you can then choose to use the central configuration or override it with custom settings.
|
||||
> 3. In addition to this centralized configuration, all VTherms can be controlled by a single entity of type `select`. This function is named `central_mode`. This allows you to stop / start / freeze / etc. all VTherms at once. For each VTherm, the user indicates whether he is affected by this `central_mode`.
|
||||
|
||||
|
||||
## Creation of a new Versatile Thermostat
|
||||
Click on Add integration button in the integration page
|
||||

|
||||
|
||||
@@ -171,7 +202,10 @@ The configuration can be change through the same interface. Simply select the th
|
||||
Then follow the configurations steps as follow:
|
||||
|
||||
## Minimal configuration update
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Give the main mandatory attributes:
|
||||
1. a name (will be the name of the integration and also the name of the climate entity)
|
||||
@@ -181,7 +215,8 @@ Give the main mandatory attributes:
|
||||
6. a cycle duration in minutes. On each cycle, the heater will cycle on and then off for a calculated time to reach the target temperature (see [preset](#configure-the-preset-temperature) below). In ```over_climate``` mode, the cycle is only used to carry out basic controls but does not directly regulate the temperature. It's the underlying climate that does it,
|
||||
7. minimum and maximum thermostat temperatures,
|
||||
8. the power of the l'équipement which will activate the power and energy sensors of the device,
|
||||
9. the list of features that will be used for this thermostat. Depending on your choices, the following configuration screens will appear or not.
|
||||
9. the possibility of controlling the thermostat centrally. Cf [centralized control](#centralized-control),
|
||||
10. the list of features that will be used for this thermostat. Depending on your choices, the following configuration screens will appear or not.
|
||||
|
||||
>  _*Notes*_
|
||||
> 1. With the ```thermostat_over_switch``` type, calculation are done at each cycle. So in case of conditions change, you will have to wait for the next cycle to see a change. For this reason, the cycle should not be too long. **5 min is a good value**,
|
||||
@@ -357,7 +392,7 @@ The preset mode allows you to pre-configurate targeted temperature. Used in conj
|
||||
> 1. Changing manually the target temperature, set the preset to None (no preset). This way you can always set a target temperature even if no preset are available.
|
||||
> 2. standard ``Away`` preset is a hidden preset which is not directly selectable. Versatile Thermostat uses the presence management or movement management to set automatically and dynamically the target temperature depending on a presence in the home or an activity in the room. See [presence management](#configure-the-presence-management).
|
||||
> 3. if you uses the power shedding management, you will see a hidden preset named ``power``. The heater preset is set to ``power`` when overpowering conditions are encountered and shedding is active for this heater. See [power management](#configure-the-power-management).
|
||||
> 4. if you uses the advanced configuration you will see the preset set to ``security`` if the temperature could not be retrieved after a certain delay
|
||||
> 4. if you uses the advanced configuration you will see the preset set to ``safety`` if the temperature could not be retrieved after a certain delay
|
||||
> 5. ff you don't want to use the preseet, give 0 as temperature. The preset will then been ignored and will not displayed in the front component
|
||||
|
||||
## Configure the doors/windows turning on/off the thermostats
|
||||
@@ -429,7 +464,7 @@ For this to work, the climate thermostat should be in ``Activity`` preset mode.
|
||||
If you choose the ```Power management``` feature, click on 'Validate' on the previous page and you will get there:
|
||||

|
||||
|
||||
This feature allows you to regulate the power consumption of your radiators. Known as shedding, this feature allows you to limit the electrical power consumption of your heater if overpowering conditions are detected. Give a **sensor to the current power consumption of your house**, a **sensor to the max power** that should not be exceeded, the **power consumption of your heater** (in the first step of the configuration) and the algorithm will not start a radiator if the max power will be exceeded after radiator starts.
|
||||
This feature allows you to regulate the power consumption of your radiators. Known as shedding, this feature allows you to limit the electrical power consumption of your heater if overpowering conditions are detected. Give a **sensor to the current power consumption of your house**, a **sensor to the max power** that should not be exceeded, the **power consumption of your heaters linked to the VTherm** (in the first step of the configuration) and the algorithm will not start a radiator if the max power will be exceeded after radiator starts.
|
||||
|
||||
|
||||
Note that all power values should have the same units (kW or W for example).
|
||||
@@ -470,88 +505,107 @@ The advanced configuration form is the following:
|
||||
|
||||
The first delay (minimal_activation_delay_sec) in sec in the minimum delay acceptable for turning on the heater. When calculation gives a power on delay below this value, the heater will stays off.
|
||||
|
||||
The second delay (security_delay_min) is the maximal delay between two temperature measure before setting the preset to ``security`` and turning off the thermostat. If the temperature sensor is no more giving temperature measures, the thermostat and heater will turns off after this delay and the preset of the thermostat will be set to ``security``. This is useful to avoid overheating is the battery of your temperature sensor is too low.
|
||||
The second delay (security_delay_min) is the maximal delay between two temperature measure before setting the preset to ``safety`` and turning off the thermostat. If the temperature sensor is no more giving temperature measures, the thermostat and heater will turns off after this delay and the preset of the thermostat will be set to ``safety``. This is useful to avoid overheating is the battery of your temperature sensor is too low.
|
||||
|
||||
The third parameter (``security_min_on_percent``) is the minimum value of ``on_percent`` below which the security preset will not be activated. This parameter makes it possible not to put a thermostat in safety, if the controlled radiator does not heat sufficiently.
|
||||
Setting this parameter to ``0.00`` will trigger the security preset regardless of the last heating setpoint, conversely ``1.00`` will never trigger the security preset (which amounts to disabling the function).
|
||||
The third parameter (``security_min_on_percent``) is the minimum value of ``on_percent`` below which the safety preset will not be activated. This parameter makes it possible not to put a thermostat in safety, if the controlled radiator does not heat sufficiently.
|
||||
Setting this parameter to ``0.00`` will trigger the safety preset regardless of the last heating setpoint, conversely ``1.00`` will never trigger the safety preset (which amounts to disabling the function).
|
||||
|
||||
The fourth parameter (``security_default_on_percent``) is the ``on_percent`` value that will be used when the thermostat enters ``security`` mode. If you put ``0`` then the thermostat will be cut off when it goes into ``security`` mode, putting 0.2% for example allows you to keep a little heating (20% in this case), even in mode ``security``. It avoids finding your home totally frozen during a thermometer failure.
|
||||
The fourth parameter (``security_default_on_percent``) is the ``on_percent`` value that will be used when the thermostat enters ``safety`` mode. If you put ``0`` then the thermostat will be cut off when it goes into ``safety`` mode, putting 0.2% for example allows you to keep a little heating (20% in this case), even in mode ``safety``. It avoids finding your home totally frozen during a thermometer failure.
|
||||
|
||||
See [example tuning](#examples-tuning) for common tuning examples
|
||||
|
||||
> _*Notes*_
|
||||
> 1. When the temperature sensor comes to life and returns the temperatures, the preset will be restored to its previous value,
|
||||
> 2. Attention, two temperatures are needed: internal temperature and external temperature and each must give the temperature, otherwise the thermostat will be in "security" preset,
|
||||
> 3. A service is available that allows you to set the 3 security parameters. This can be used to adapt the security function to your use.
|
||||
> 2. Attention, two temperatures are needed: internal temperature and external temperature and each must give the temperature, otherwise the thermostat will be in "safety" preset,
|
||||
> 3. A service is available that allows you to set the 3 safety parameters. This can be used to adapt the safety function to your use.
|
||||
> 4. For natural usage, the ``security_default_on_percent`` should be less than ``security_min_on_percent``,
|
||||
> 5. Thermostat of type ``thermostat_over_climate`` are not concerned by the security feature.
|
||||
> 5. Thermostat of type ``thermostat_over_climate`` are not concerned by the safety feature.
|
||||
|
||||
## Centralized control
|
||||
Since release 5.2, if you have defined a centralized configuration, you have a new entity named `select.central_mode` which allows you to control all VTherms with a single action. For a VTherm to be centrally controllable, its configuration attribute named `use_central_mode` must be true.
|
||||
|
||||
This entity is presented in the form of a list of choices which contains the following choices:
|
||||
1. `Auto`: the 'normal' mode in which each VTherm behaves as in previous versions,
|
||||
2. `Stooped`: all VTherms are turned off (`hvac_off`),
|
||||
3. `Heat only`: all VTherms are put in heating mode when this mode is supported by the VTherm, otherwise it is stopped,
|
||||
3. `Cool only`: all VTherms are put in cooling mode when this mode is supported by the VTherm, otherwise it is stopped,
|
||||
4. `Frost protection`: all VTherms are put in frost protection preset when this preset is supported by the VTherm, otherwise it is stopped.
|
||||
|
||||
It is therefore possible to control all VTherms (only those explicitly designated) with a single control.
|
||||
Example rendering:
|
||||
|
||||

|
||||
|
||||
## 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" | "central configuration" |
|
||||
| ----------------------------------------- | ----------------------------------------------------------------------------- | ------------- | ------------------- | ------------ | ----------------------- |
|
||||
| ``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 | X |
|
||||
| ``cycle_min`` | Cycle duration (minutes) | X | X | X | - |
|
||||
| ``temp_min`` | Minimal temperature allowed | X | X | X | X |
|
||||
| ``temp_max`` | Maximal temperature allowed | X | X | X | X |
|
||||
| ``device_power`` | Total device power | X | X | X | - |
|
||||
| ``use_central_mode`` | Allow the centralized control | 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 | X |
|
||||
| ``tpi_coef_ext`` | Coefficient to use for external temperature delta | X | - | X | X |
|
||||
| ``frost_temp`` | Temperature in frost protection preset | X | X | X | X |
|
||||
| ``eco_temp`` | Temperature in Eco preset | X | X | X | X |
|
||||
| ``comfort_temp`` | Temperature in Comfort preset | X | X | X | X |
|
||||
| ``boost_temp`` | Temperature in Boost preset | X | X | X | X |
|
||||
| ``eco_ac_temp`` | Temperature in Eco preset for AC mode | X | X | X | X |
|
||||
| ``comfort_ac_temp`` | Temperature in Comfort preset for AC mode | X | X | X | X |
|
||||
| ``boost_ac_temp`` | Temperature in Boost preset for AC mode | X | X | X | X |
|
||||
| ``window_sensor_entity_id`` | Window sensor entity id | X | X | X | - |
|
||||
| ``window_delay`` | Window sensor delay (seconds) | X | X | X | X |
|
||||
| ``window_auto_open_threshold`` | Temperature decrease threshold for automatic window open detection (in °/min) | X | X | X | X |
|
||||
| ``window_auto_close_threshold`` | Temperature increase threshold for end of automatic detection (in °/min) | X | X | X | X |
|
||||
| ``window_auto_max_duration`` | Maximum duration of automatic window open detection (in min) | X | 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 | X |
|
||||
| ``motion_off_delay`` | Delay before considering the end of motion (seconds) | X | X | X | X |
|
||||
| ``motion_preset`` | Preset to use when motion is detected | X | X | X | X |
|
||||
| ``no_motion_preset`` | Preset to use when no motion is detected | X | X | X | X |
|
||||
| ``power_sensor_entity_id`` | Power sensor entity id | X | X | X | X |
|
||||
| ``max_power_sensor_entity_id`` | Max power sensor entity id | X | X | X | X |
|
||||
| ``power_temp`` | Temperature for Power shedding | X | X | X | X |
|
||||
| ``presence_sensor_entity_id`` | Presence sensor entity id | X | X | X | X |
|
||||
| ``frost_away_temp`` | Temperature in Frost protection preset when no presence | X | X | X | X |
|
||||
| ``eco_away_temp`` | Temperature in Eco preset when no presence | X | X | X | X |
|
||||
| ``comfort_away_temp`` | Temperature in Comfort preset when no presence | X | X | X | X |
|
||||
| ``boost_away_temp`` | Temperature in Boost preset when no presence | X | X | X | X |
|
||||
| ``eco_ac_away_temp`` | Temperature in Eco preset when no presence in AC mode | X | X | X | X |
|
||||
| ``comfort_ac_away_temp`` | Temperature in Comfort preset when no presence in AC mode | X | X | X | X |
|
||||
| ``boost_ac_away_temp`` | Temperature in Boost preset when no presence in AC mode | X | X | X | X |
|
||||
| ``minimal_activation_delay`` | Minimal activation delay | X | - | X | X |
|
||||
| ``security_delay_min`` | Safety delay (in minutes) | X | - | X | X |
|
||||
| ``security_min_on_percent`` | Minimal power percent to enable safety mode | X | - | X | X |
|
||||
| ``security_default_on_percent`` | Power percent to use in safety mode | X | - | 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 | - | - | - |
|
||||
| ``auto_fan_mode` | Auto fan mode | - | X | - | - |
|
||||
|
||||
# Examples tuning
|
||||
|
||||
@@ -565,8 +619,8 @@ See [example tuning](#examples-tuning) for common tuning examples
|
||||
|
||||
## Temperature sensor will battery
|
||||
- security_delay_min: 60 min (because these sensors are lazy)
|
||||
- security_min_on_percent: 0.5 (50% - we go to the ``security`` preset if the radiator was heating more than 50% of the time)
|
||||
- security_default_on_percent: 0.1 (10% - in preset ``security``, we keep a heating background 20% of the time)
|
||||
- security_min_on_percent: 0.5 (50% - we go to the ``safety`` preset if the radiator was heating more than 50% of the time)
|
||||
- security_default_on_percent: 0.1 (10% - in preset ``safety``, we keep a heating background 20% of the time)
|
||||
|
||||
These settings should be understood as follows:
|
||||
|
||||
@@ -580,16 +634,18 @@ Versatile Thermostat allows you to be notified when an event of this type occurs
|
||||
|
||||
## Reactive temperature sensor (on mains)
|
||||
- security_delay_min: 15min
|
||||
- security_min_on_percent: 0.7 (70% - we go to the ``security`` preset if the radiator was heating more than 70% of the time)
|
||||
- security_default_on_percent: 0.25 (25% - in preset ``security``, we keep a heating background 25% of the time)
|
||||
- security_min_on_percent: 0.7 (70% - we go to the ``safety`` preset if the radiator was heating more than 70% of the time)
|
||||
- security_default_on_percent: 0.25 (25% - in preset ``safety``, we keep a heating background 25% of the time)
|
||||
|
||||
## My preset configuration
|
||||
This is just an example of how I use the preset. It up to you to adapt to your configuration but it can be useful to understand how it works.
|
||||
``Frost``: 10 °C
|
||||
``Eco``: 17 °C
|
||||
``Comfort``: 19 °C
|
||||
``Boost``: 20 °C
|
||||
|
||||
When presence if off:
|
||||
``Frost``: 10 °C
|
||||
``Eco``: 16.5 °C
|
||||
``Comfort``: 17 °C
|
||||
``Boost``: 18 °C
|
||||
@@ -638,7 +694,7 @@ In order, there are:
|
||||
8. load shedding status,
|
||||
9. cycle power percentage (TPI only),
|
||||
10. presence status (if presence management is configured),
|
||||
11. security status,
|
||||
11. safety status,
|
||||
12. opening status (if opening management is configured),
|
||||
13. motion status (if motion management is configured),
|
||||
14. the valve opening percentage (for the `over_valve` type)
|
||||
@@ -707,11 +763,11 @@ target:
|
||||
>  _*Notes*_
|
||||
- after a restart the preset are resetted to the configured temperature. If you want your change to be permanent you should modify the temperature preset into the confguration of the integration.
|
||||
|
||||
## Change security settings
|
||||
This service is used to dynamically modify the security parameters described here [Advanced configuration](#configuration-avanced).
|
||||
If the thermostat is in ``security`` mode the new settings are applied immediately.
|
||||
## Change safety settings
|
||||
This service is used to dynamically modify the safety parameters described here [Advanced configuration](#configuration-avanced).
|
||||
If the thermostat is in ``safety`` mode the new settings are applied immediately.
|
||||
|
||||
To change the security settings use the following code:
|
||||
To change the safety settings use the following code:
|
||||
```
|
||||
service : thermostat_polyvalent.set_security
|
||||
data:
|
||||
@@ -738,7 +794,7 @@ target:
|
||||
Significant thermostat events are notified via the message bus.
|
||||
The notified events are as follows:
|
||||
|
||||
- ``versatile_thermostat_security_event``: a thermostat enters or exits the ``security`` preset
|
||||
- ``versatile_thermostat_security_event``: a thermostat enters or exits the ``safety`` preset
|
||||
- ``versatile_thermostat_power_event``: a thermostat enters or exits the ``power`` preset
|
||||
- ``versatile_thermostat_temperature_event``: one or both temperature measurements of a thermostat have not been updated for more than ``security_delay_min`` minutes
|
||||
- ``versatile_thermostat_hvac_mode_event``: the thermostat is on or off. This event is also broadcast when the thermostat starts up
|
||||
@@ -746,7 +802,7 @@ The notified events are as follows:
|
||||
|
||||
If you have followed correctly, when a thermostat goes into safety mode, 3 events are triggered:
|
||||
1. ``versatile_thermostat_temperature_event`` to indicate that a thermometer has become unresponsive,
|
||||
2. ``versatile_thermostat_preset_event`` to indicate the switch to ```security``` preset,
|
||||
2. ``versatile_thermostat_preset_event`` to indicate the switch to ```safety``` preset,
|
||||
3. ``versatile_thermostat_hvac_mode_event`` to indicate the possible extinction of the thermostat
|
||||
|
||||
Each event carries the key values of the event (temperatures, current preset, current power, etc.) as well as the states of the thermostat.
|
||||
@@ -787,12 +843,12 @@ Custom attributes are the following:
|
||||
| ``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`` |
|
||||
| ``security_delay_min`` | The delay before setting the safety mode when temperature sensor are off |
|
||||
| ``security_min_on_percent`` | The minimal on_percent below which safety preset won't be trigger |
|
||||
| ``security_default_on_percent`` | The on_percent used when thermostat is in ``safety`` |
|
||||
| ``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 |
|
||||
| ``security_state`` | The safety 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 |
|
||||
@@ -800,6 +856,8 @@ Custom attributes are the following:
|
||||
| ``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) |
|
||||
| ``is_controlled_by_central_mode`` | True if the VTherm can be centrally controlled |
|
||||
| ``last_central_mode`` | The last central mode used (None if the VTherm is not centrally controlled) |
|
||||
|
||||
# Some results
|
||||
|
||||
@@ -972,6 +1030,11 @@ Replace values in [[ ]] by yours.
|
||||
step: day
|
||||
```
|
||||
|
||||
Example of graph obtained with Plotly :
|
||||
|
||||

|
||||
|
||||
|
||||
## And always better and better with the NOTIFIER daemon app to notify events
|
||||
This automation uses the excellent App Daemon named NOTIFIER developed by Horizon Domotique that you will find in demonstration [here](https://www.youtube.com/watch?v=chJylIK0ASo&ab_channel=HorizonDomotique) and the code is [here](https ://github.com/jlpouffier/home-assistant-config/blob/master/appdaemon/apps/notifier.py). It allows you to notify the users of the accommodation when one of the events affecting safety occurs on one of the Versatile Thermostats.
|
||||
|
||||
@@ -1117,7 +1180,19 @@ Example :
|
||||
|
||||
## Only the first radiator heats
|
||||
In `over_switch` mode if several radiators are configured for the same VTherm, switching on will be done sequentially to smooth out consumption peaks as much as possible.
|
||||
This is completely normal and desired. It is described here: [For a thermostat of type ``thermostat_over_switch```](#for-a-thermostat-of-type-thermostat_over_switch)
|
||||
This is completely normal and desired. It is described here: [For a thermostat of type ``thermostat_over_switch```](#for-a-thermostat-of-type-thermostat_over_switch)v
|
||||
|
||||
## The radiator heats up even though the setpoint temperature is exceeded or does not heat up even though the room temperature is well below the setpoint
|
||||
|
||||
### Type `over_switch` or `over_valve`
|
||||
With a VTherm of type `over_switch` or `over_valve`, this fault just shows that the parameters of the TPI algorithm are incorrectly set. See [TPI Algorithm](#tpi-algorithm) to optimize the settings.
|
||||
|
||||
### Type `over_climate`
|
||||
With an `over_climate` type VTherm, the regulation is done by the underlying `climate` directly and VTherm simply transmits the instructions to it. So if the radiator heats up when the set temperature is exceeded, it is certainly because its internal temperature measurement is biased. This happens very often with TRVs and reversible air conditioning units which have an internal temperature sensor, or too close to the heating element (therefore too cold in winter).
|
||||
|
||||
Example of discussion around these topics: [#316](https://github.com/jmcollin78/versatile_thermostat/issues/316), [#312](https://github.com/jmcollin78/versatile_thermostat/discussions/312 ), [#278](https://github.com/jmcollin78/versatile_thermostat/discussions/278)
|
||||
|
||||
To get around this, VTherm is equipped with a function called self-regulation which allows the instruction sent to the underlying to be adapted until the target temperature is respected. This function compensates for the measurement bias of internal thermometers. If the bias is important the regulation must be important. See [Self-regulation](#self-regulation) to configure self-regulation.
|
||||
|
||||
## Adjust window opening detection parameters in auto mode
|
||||
|
||||
@@ -1146,15 +1221,15 @@ As the algorithm is based on temperature measurements, if they are no longer rec
|
||||
|
||||
All these parameters are adjusted on the last page of the VTherm configuration: “Advanced parameters”.
|
||||
|
||||
### How to detect security mode?
|
||||
### How to detect safety 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)
|
||||
[safety 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)
|
||||
[safety 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.
|
||||
|
||||
@@ -1169,13 +1244,13 @@ security_delay_min: 60
|
||||
```
|
||||
|
||||
We see that :
|
||||
1. the VTherm is in security mode (`security_state: true`),
|
||||
1. the VTherm is in safety 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`)
|
||||
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 safety 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 be notified, the VTherm sends an event as soon as it happens and one at the end of the safety 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).
|
||||
|
||||
@@ -1184,7 +1259,7 @@ 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.
|
||||
4. As soon as the temperature is received again the safety mode will be removed and the previous values of preset, target temperature and mode will be restored.
|
||||
|
||||
## Using a group of people as a presence sensor
|
||||
Unfortunately, groups of people are not recognized as presence sensors. We cannot therefore use them directly in VTherm.
|
||||
|
||||
@@ -3,10 +3,13 @@ from __future__ import annotations
|
||||
|
||||
from typing import Dict
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from homeassistant.const import SERVICE_RELOAD
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigType
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
@@ -21,6 +24,8 @@ from .const import (
|
||||
CONF_AUTO_REGULATION_SLOW,
|
||||
CONF_AUTO_REGULATION_EXPERT,
|
||||
CONF_SHORT_EMA_PARAMS,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
)
|
||||
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
@@ -65,6 +70,10 @@ async def async_setup(
|
||||
config.get(DOMAIN),
|
||||
)
|
||||
|
||||
async def _handle_reload(_):
|
||||
"""The reload callback"""
|
||||
await reload_all_vtherm(hass)
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
# L'argument config contient votre fichier configuration.yaml
|
||||
@@ -76,9 +85,28 @@ async def async_setup(
|
||||
else:
|
||||
_LOGGER.info("No global config from configuration.yaml available")
|
||||
|
||||
hass.helpers.service.async_register_admin_service(
|
||||
DOMAIN,
|
||||
SERVICE_RELOAD,
|
||||
_handle_reload,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def reload_all_vtherm(hass):
|
||||
"""Handle reload service call."""
|
||||
_LOGGER.info("Service %s.reload called: reloading integration", DOMAIN)
|
||||
|
||||
current_entries = hass.config_entries.async_entries(DOMAIN)
|
||||
|
||||
reload_tasks = [
|
||||
hass.config_entries.async_reload(entry.entry_id) for entry in current_entries
|
||||
]
|
||||
|
||||
await asyncio.gather(*reload_tasks)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Versatile Thermostat from a config entry."""
|
||||
|
||||
@@ -101,7 +129,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
if entry.data.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
await reload_all_vtherm(hass)
|
||||
else:
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
@@ -99,6 +99,14 @@ from .const import (
|
||||
DEFAULT_SECURITY_MIN_ON_PERCENT,
|
||||
DEFAULT_SECURITY_DEFAULT_ON_PERCENT,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
HIDDEN_PRESETS,
|
||||
@@ -108,8 +116,15 @@ from .const import (
|
||||
ATTR_TOTAL_ENERGY,
|
||||
PRESET_AC_SUFFIX,
|
||||
DEFAULT_SHORT_EMA_PARAMS,
|
||||
CENTRAL_MODE_AUTO,
|
||||
CENTRAL_MODE_STOPPED,
|
||||
CENTRAL_MODE_HEAT_ONLY,
|
||||
CENTRAL_MODE_COOL_ONLY,
|
||||
CENTRAL_MODE_FROST_PROTECTION,
|
||||
)
|
||||
|
||||
from .config_schema import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
from .underlyings import UnderlyingEntity
|
||||
|
||||
@@ -148,6 +163,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
frozenset(
|
||||
{
|
||||
"is_on",
|
||||
"is_controlled_by_central_mode",
|
||||
"last_central_mode",
|
||||
"type",
|
||||
"frost_temp",
|
||||
"eco_temp",
|
||||
@@ -263,17 +280,73 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._now = None
|
||||
|
||||
self._attr_fan_mode = None
|
||||
|
||||
self._is_central_mode = None
|
||||
self._last_central_mode = None
|
||||
self.post_init(entry_infos)
|
||||
|
||||
def post_init(self, entry_infos):
|
||||
def clean_central_config_doublon(self, config_entry, central_config) -> dict:
|
||||
"""Removes all values from config with are concerned by central_config"""
|
||||
|
||||
def clean_one(cfg, schema: vol.Schema):
|
||||
"""Clean one schema"""
|
||||
for key, _ in schema.schema.items():
|
||||
if key in cfg:
|
||||
del cfg[key]
|
||||
|
||||
cfg = config_entry.copy()
|
||||
if central_config and central_config.data:
|
||||
# Removes config if central is used
|
||||
if cfg.get(CONF_USE_MAIN_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_MAIN_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_TPI_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_TPI_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_PRESETS_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_PRESETS_DATA_SCHEMA)
|
||||
clean_one(cfg, STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_WINDOW_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_WINDOW_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_MOTION_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_MOTION_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_POWER_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_POWER_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_PRESENCE_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_PRESENCE_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_ADVANCED_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_ADVANCED_DATA_SCHEMA)
|
||||
|
||||
# take all central config
|
||||
entry_infos = central_config.data.copy()
|
||||
# and merge with cleaned config_entry
|
||||
entry_infos.update(cfg)
|
||||
else:
|
||||
entry_infos = cfg
|
||||
|
||||
return entry_infos
|
||||
|
||||
def post_init(self, config_entry):
|
||||
"""Finish the initialization of the thermostast"""
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - Updating VersatileThermostat with infos %s",
|
||||
self,
|
||||
entry_infos,
|
||||
config_entry,
|
||||
)
|
||||
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self._hass)
|
||||
central_config = api.find_central_configuration()
|
||||
|
||||
entry_infos = self.clean_central_config_doublon(config_entry, central_config)
|
||||
|
||||
_LOGGER.info("%s - The merged configuration is %s", self, entry_infos)
|
||||
|
||||
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)
|
||||
@@ -337,7 +410,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
)
|
||||
self._window_auto_max_duration = entry_infos.get(CONF_WINDOW_AUTO_MAX_DURATION)
|
||||
self._window_auto_on = (
|
||||
self._window_auto_open_threshold is not None
|
||||
self._window_sensor_entity_id is None
|
||||
and self._window_auto_open_threshold is not None
|
||||
and self._window_auto_open_threshold > 0.0
|
||||
and self._window_auto_close_threshold is not None
|
||||
and self._window_auto_max_duration is not None
|
||||
@@ -371,6 +445,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._presence_on = self._presence_sensor_entity_id is not None
|
||||
|
||||
if self._ac_mode:
|
||||
# Added by https://github.com/jmcollin78/versatile_thermostat/pull/144
|
||||
# Some over_switch can do both heating and cooling
|
||||
self._hvac_list = [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF]
|
||||
else:
|
||||
self._hvac_list = [HVACMode.HEAT, HVACMode.OFF]
|
||||
@@ -475,8 +551,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._total_energy = 0
|
||||
|
||||
# Read the parameter from configuration.yaml if it exists
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self._hass)
|
||||
|
||||
short_ema_params = DEFAULT_SHORT_EMA_PARAMS
|
||||
if api is not None and api.short_ema_params:
|
||||
short_ema_params = api.short_ema_params
|
||||
@@ -491,6 +565,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
short_ema_params.get("max_alpha"),
|
||||
)
|
||||
|
||||
self._is_central_mode = not (
|
||||
entry_infos.get(CONF_USE_CENTRAL_MODE) is False
|
||||
) # Default value (None) is True
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s - Creation of a new VersatileThermostat entity: unique_id=%s",
|
||||
self,
|
||||
@@ -1069,6 +1147,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"""True if the VTherm is on (! HVAC_OFF)"""
|
||||
return self.hvac_mode and self.hvac_mode != HVACMode.OFF
|
||||
|
||||
@property
|
||||
def is_controlled_by_central_mode(self) -> bool:
|
||||
"""Returns True if this VTherm can be controlled by the central_mode"""
|
||||
return self._is_central_mode
|
||||
|
||||
@property
|
||||
def last_central_mode(self) -> str | None:
|
||||
"""Returns the last central_mode taken into account.
|
||||
Is None if the VTherm is not controlled by central_mode"""
|
||||
return self._last_central_mode
|
||||
|
||||
def underlying_entity_id(self, index=0) -> str | None:
|
||||
"""The climate_entity_id. Added for retrocompatibility reason"""
|
||||
if index < self.nb_underlying_entities:
|
||||
@@ -1116,11 +1205,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
)
|
||||
|
||||
# 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:
|
||||
if self._hvac_mode == HVACMode.COOL:
|
||||
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)
|
||||
await self._async_set_preset_mode_internal(PRESET_ECO, True, False)
|
||||
|
||||
if need_control_heating and sub_need_control_heating:
|
||||
await self.async_control_heating(force=True)
|
||||
@@ -1134,12 +1223,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self.async_write_ha_state()
|
||||
self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode})
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode):
|
||||
@overrides
|
||||
async def async_set_preset_mode(self, preset_mode, overwrite_saved_preset=True):
|
||||
"""Set new preset mode."""
|
||||
await self._async_set_preset_mode_internal(preset_mode)
|
||||
await self._async_set_preset_mode_internal(
|
||||
preset_mode, force=False, overwrite_saved_preset=overwrite_saved_preset
|
||||
)
|
||||
await self.async_control_heating(force=True)
|
||||
|
||||
async def _async_set_preset_mode_internal(self, preset_mode, force=False):
|
||||
async def _async_set_preset_mode_internal(
|
||||
self, preset_mode, force=False, overwrite_saved_preset=True
|
||||
):
|
||||
"""Set new preset mode."""
|
||||
_LOGGER.info("%s - Set preset_mode: %s force=%s", self, preset_mode, force)
|
||||
if (
|
||||
@@ -1154,10 +1248,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
# I don't think we need to call async_write_ha_state if we didn't change the state
|
||||
return
|
||||
|
||||
# In security mode don't change preset but memorise the new expected preset when security will be off
|
||||
# In safety mode don't change preset but memorise the new expected preset when security will be off
|
||||
if preset_mode != PRESET_SECURITY and self._security_state:
|
||||
_LOGGER.debug(
|
||||
"%s - is in security mode. Just memorise the new expected ", self
|
||||
"%s - is in safety mode. Just memorise the new expected ", self
|
||||
)
|
||||
if preset_mode not in HIDDEN_PRESETS:
|
||||
self._saved_preset_mode = preset_mode
|
||||
@@ -1181,7 +1275,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
self.reset_last_temperature_time(old_preset_mode)
|
||||
|
||||
self.save_preset_mode()
|
||||
if overwrite_saved_preset:
|
||||
self.save_preset_mode()
|
||||
self.recalculate()
|
||||
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||
|
||||
@@ -1311,9 +1406,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
return
|
||||
|
||||
await self._async_update_temp(new_state)
|
||||
dearm_window_auto = await self._async_update_temp(new_state)
|
||||
self.recalculate()
|
||||
await self.async_control_heating(force=False)
|
||||
return dearm_window_auto
|
||||
|
||||
async def _async_ext_temperature_changed(self, event: Event):
|
||||
"""Handle external temperature opf the sensor changes."""
|
||||
@@ -1381,16 +1477,19 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
else:
|
||||
if not self._window_state:
|
||||
_LOGGER.info(
|
||||
"%s - Window is closed. Restoring hvac_mode '%s'",
|
||||
"%s - Window is closed. Restoring hvac_mode '%s' if central_mode is not STOPPED",
|
||||
self,
|
||||
self._saved_hvac_mode,
|
||||
)
|
||||
await self.restore_hvac_mode(True)
|
||||
if self.last_central_mode != CENTRAL_MODE_STOPPED:
|
||||
await self.restore_hvac_mode(True)
|
||||
elif self._window_state:
|
||||
_LOGGER.info(
|
||||
"%s - Window is open. Set hvac_mode to '%s'", self, HVACMode.OFF
|
||||
)
|
||||
self.save_hvac_mode()
|
||||
if self.last_central_mode in [CENTRAL_MODE_AUTO, None]:
|
||||
self.save_hvac_mode()
|
||||
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
self.update_custom_attributes()
|
||||
|
||||
@@ -1543,12 +1642,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
state.last_changed.astimezone(self._current_tz),
|
||||
)
|
||||
|
||||
# try to restart if we were in security mode
|
||||
# try to restart if we were in safety mode
|
||||
if self._security_state:
|
||||
await self.check_security()
|
||||
|
||||
# check window_auto
|
||||
await self._async_manage_window_auto()
|
||||
return await self._async_manage_window_auto()
|
||||
|
||||
except ValueError as ex:
|
||||
_LOGGER.error("Unable to update temperature from sensor: %s", ex)
|
||||
@@ -1570,7 +1669,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
state.last_changed.astimezone(self._current_tz),
|
||||
)
|
||||
|
||||
# try to restart if we were in security mode
|
||||
# try to restart if we were in safety mode
|
||||
if self._security_state:
|
||||
await self.check_security()
|
||||
except ValueError as ex:
|
||||
@@ -1755,8 +1854,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
slope if slope is not None else 0.0,
|
||||
)
|
||||
|
||||
if self.window_bypass_state:
|
||||
_LOGGER.info("%s - Window auto event is ignored because bypass is ON", self)
|
||||
if self.window_bypass_state or not self.is_window_auto_enabled:
|
||||
_LOGGER.info("%s - Window auto event is ignored because bypass is ON or window auto detection is disabled", self)
|
||||
return
|
||||
|
||||
if (
|
||||
@@ -1937,6 +2036,67 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
return self._overpowering_state
|
||||
|
||||
async def check_central_mode(self, new_central_mode, old_central_mode) -> None:
|
||||
"""Take into account a central mode change"""
|
||||
if not self.is_controlled_by_central_mode:
|
||||
self._last_central_mode = None
|
||||
return
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - Central mode have change from %s to %s",
|
||||
self,
|
||||
old_central_mode,
|
||||
new_central_mode,
|
||||
)
|
||||
|
||||
self._last_central_mode = new_central_mode
|
||||
|
||||
def save_all():
|
||||
"""save preset and hvac_mode"""
|
||||
self.save_preset_mode()
|
||||
self.save_hvac_mode()
|
||||
|
||||
if new_central_mode == CENTRAL_MODE_AUTO:
|
||||
if self.window_state is not STATE_ON:
|
||||
await self.restore_hvac_mode()
|
||||
await self.restore_preset_mode()
|
||||
|
||||
return
|
||||
|
||||
if old_central_mode == CENTRAL_MODE_AUTO and self.window_state is not STATE_ON:
|
||||
save_all()
|
||||
|
||||
if new_central_mode == CENTRAL_MODE_STOPPED:
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
return
|
||||
|
||||
if new_central_mode == CENTRAL_MODE_COOL_ONLY:
|
||||
if HVACMode.COOL in self.hvac_modes:
|
||||
await self.async_set_hvac_mode(HVACMode.COOL)
|
||||
else:
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
return
|
||||
|
||||
if new_central_mode == CENTRAL_MODE_HEAT_ONLY:
|
||||
if HVACMode.HEAT in self.hvac_modes:
|
||||
await self.async_set_hvac_mode(HVACMode.HEAT)
|
||||
else:
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
return
|
||||
|
||||
if new_central_mode == CENTRAL_MODE_FROST_PROTECTION:
|
||||
if (
|
||||
PRESET_FROST_PROTECTION in self.preset_modes
|
||||
and HVACMode.HEAT in self.hvac_modes
|
||||
):
|
||||
await self.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await self.async_set_preset_mode(
|
||||
PRESET_FROST_PROTECTION, overwrite_saved_preset=False
|
||||
)
|
||||
else:
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
return
|
||||
|
||||
def _set_now(self, now: datetime):
|
||||
"""Set the now timestamp. This is only for tests purpose"""
|
||||
self._now = now
|
||||
@@ -2003,7 +2163,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
if shouldStartSecurity:
|
||||
if shouldClimateBeInSecurity:
|
||||
_LOGGER.warning(
|
||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Set it into security mode",
|
||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Setting it into safety mode",
|
||||
self,
|
||||
self._security_delay_min,
|
||||
delta_temp,
|
||||
@@ -2012,13 +2172,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
)
|
||||
elif shouldSwitchBeInSecurity:
|
||||
_LOGGER.warning(
|
||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f) is over defined value (%.2f). Set it into security mode",
|
||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f %%) is over defined value (%.2f %%). Set it into safety mode",
|
||||
self,
|
||||
self._security_delay_min,
|
||||
delta_temp,
|
||||
delta_ext_temp,
|
||||
self._prop_algorithm.on_percent,
|
||||
self._security_min_on_percent,
|
||||
self._prop_algorithm.on_percent * 100,
|
||||
self._security_min_on_percent * 100,
|
||||
)
|
||||
|
||||
self.send_event(
|
||||
@@ -2036,7 +2196,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
},
|
||||
)
|
||||
|
||||
# Start security mode
|
||||
# Start safety mode
|
||||
if shouldStartSecurity:
|
||||
self._security_state = True
|
||||
self.save_hvac_mode()
|
||||
@@ -2064,10 +2224,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
},
|
||||
)
|
||||
|
||||
# Stop security mode
|
||||
# Stop safety mode
|
||||
if shouldStopSecurity:
|
||||
_LOGGER.warning(
|
||||
"%s - End of security mode. restoring hvac_mode to %s and preset_mode to %s",
|
||||
"%s - End of safety mode. restoring hvac_mode to %s and preset_mode to %s",
|
||||
self,
|
||||
self._saved_hvac_mode,
|
||||
self._saved_preset_mode,
|
||||
@@ -2178,6 +2338,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"hvac_mode": self.hvac_mode,
|
||||
"preset_mode": self.preset_mode,
|
||||
"type": self._thermostat_type,
|
||||
"is_controlled_by_central_mode": self.is_controlled_by_central_mode,
|
||||
"last_central_mode": self.last_central_mode,
|
||||
"frost_temp": self._presets[PRESET_FROST_PROTECTION],
|
||||
"eco_temp": self._presets[PRESET_ECO],
|
||||
"boost_temp": self._presets[PRESET_BOOST],
|
||||
@@ -2313,11 +2475,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
entity_id: climate.thermostat_2
|
||||
"""
|
||||
_LOGGER.info(
|
||||
"%s - Calling service_set_security, delay_min: %s, min_on_percent: %s, default_on_percent: %s",
|
||||
"%s - Calling service_set_security, delay_min: %s, min_on_percent: %s %%, default_on_percent: %s %%",
|
||||
self,
|
||||
delay_min,
|
||||
min_on_percent,
|
||||
default_on_percent,
|
||||
min_on_percent * 100,
|
||||
default_on_percent * 100,
|
||||
)
|
||||
if delay_min:
|
||||
self._security_delay_min = delay_min
|
||||
|
||||
@@ -20,6 +20,8 @@ from .const import (
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -37,8 +39,15 @@ async def async_setup_entry(
|
||||
|
||||
unique_id = entry.entry_id
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
|
||||
entities = [SecurityBinarySensor(hass, unique_id, name, entry.data),WindowByPassBinarySensor(hass, unique_id, name, entry.data)]
|
||||
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
return
|
||||
|
||||
entities = [
|
||||
SecurityBinarySensor(hass, unique_id, name, entry.data),
|
||||
WindowByPassBinarySensor(hass, unique_id, name, entry.data),
|
||||
]
|
||||
if entry.data.get(CONF_USE_MOTION_FEATURE):
|
||||
entities.append(MotionBinarySensor(hass, unique_id, name, entry.data))
|
||||
if entry.data.get(CONF_USE_WINDOW_FEATURE):
|
||||
@@ -55,8 +64,12 @@ class SecurityBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
"""Representation of a BinarySensor which exposes the security state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the SecurityState Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Security state"
|
||||
@@ -90,8 +103,12 @@ class OverpoweringBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity
|
||||
"""Representation of a BinarySensor which exposes the overpowering state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the OverpoweringState Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Overpowering state"
|
||||
@@ -125,8 +142,12 @@ class WindowBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
"""Representation of a BinarySensor which exposes the window state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the WindowState Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Window state"
|
||||
@@ -171,8 +192,12 @@ class MotionBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
"""Representation of a BinarySensor which exposes the motion state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the MotionState Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Motion state"
|
||||
@@ -207,8 +232,12 @@ class PresenceBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
"""Representation of a BinarySensor which exposes the presence state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the PresenceState Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Presence state"
|
||||
@@ -239,13 +268,18 @@ class PresenceBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
else:
|
||||
return "mdi:nature-people"
|
||||
|
||||
#PR - Adding Window ByPass
|
||||
|
||||
# PR - Adding Window ByPass
|
||||
class WindowByPassBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
"""Representation of a BinarySensor which exposes the Window ByPass state"""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, entry_infos
|
||||
) -> None: # pylint: disable=unused-argument
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name, # pylint: disable=unused-argument
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the WindowByPass Binary sensor"""
|
||||
super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
self._attr_name = "Window bypass"
|
||||
@@ -272,4 +306,4 @@ class WindowByPassBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity
|
||||
if self._attr_is_on:
|
||||
return "mdi:window-shutter-cog"
|
||||
else:
|
||||
return "mdi:window-shutter-auto"
|
||||
return "mdi:window-shutter-auto"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# pylint: disable=line-too-long
|
||||
# pylint: disable=invalid-name
|
||||
""" Implements the VersatileThermostat climate component """
|
||||
import logging
|
||||
|
||||
@@ -37,6 +35,7 @@ from .const import (
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_VALVE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
from .thermostat_switch import ThermostatOverSwitch
|
||||
@@ -64,7 +63,11 @@ async def async_setup_entry(
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
|
||||
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
return
|
||||
|
||||
# Instantiate the right base class
|
||||
entity = None
|
||||
if vt_type == CONF_THERMOSTAT_SWITCH:
|
||||
entity = ThermostatOverSwitch(hass, unique_id, name, entry.data)
|
||||
elif vt_type == CONF_THERMOSTAT_CLIMATE:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
293
custom_components/versatile_thermostat/config_schema.py
Normal file
293
custom_components/versatile_thermostat/config_schema.py
Normal file
@@ -0,0 +1,293 @@
|
||||
""" All the schemas for ConfigFlow validation"""
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
|
||||
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.input_boolean import (
|
||||
DOMAIN as INPUT_BOOLEAN_DOMAIN,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.input_number import (
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
)
|
||||
|
||||
from homeassistant.components.person import DOMAIN as PERSON_DOMAIN
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||
|
||||
|
||||
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(
|
||||
CONF_THERMOSTAT_TYPE, default=CONF_THERMOSTAT_SWITCH
|
||||
): selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=CONF_THERMOSTAT_TYPES, translation_key="thermostat_type"
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
STEP_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_TEMP_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
|
||||
vol.Optional(CONF_DEVICE_POWER, default="1"): vol.Coerce(float),
|
||||
vol.Optional(CONF_USE_CENTRAL_MODE, default=True): cv.boolean,
|
||||
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_PRESENCE_FEATURE, default=False): cv.boolean,
|
||||
vol.Required(CONF_USE_MAIN_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_HEATER): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_HEATER_2): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_HEATER_3): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_HEATER_4): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]),
|
||||
),
|
||||
vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In(
|
||||
[
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
),
|
||||
vol.Optional(CONF_AC_MODE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_INVERSE_SWITCH, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_THERMOSTAT_CLIMATE = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_CLIMATE): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
|
||||
),
|
||||
vol.Optional(CONF_CLIMATE_2): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
|
||||
),
|
||||
vol.Optional(CONF_CLIMATE_3): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
|
||||
),
|
||||
vol.Optional(CONF_CLIMATE_4): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
|
||||
),
|
||||
vol.Optional(CONF_AC_MODE, default=False): cv.boolean,
|
||||
vol.Optional(
|
||||
CONF_AUTO_REGULATION_MODE, default=CONF_AUTO_REGULATION_NONE
|
||||
): selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=CONF_AUTO_REGULATION_MODES,
|
||||
translation_key="auto_regulation_mode",
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_AUTO_REGULATION_DTEMP, default=0.5): vol.Coerce(float),
|
||||
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",
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_THERMOSTAT_VALVE = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_VALVE): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_VALVE_2): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_VALVE_3): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_VALVE_4): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In(
|
||||
[
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
),
|
||||
vol.Optional(CONF_AC_MODE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_TPI_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_USE_TPI_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_TPI_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_TPI_COEF_INT, default=0.6): vol.Coerce(float),
|
||||
vol.Required(CONF_TPI_COEF_EXT, default=0.01): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_PRESETS_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_USE_PRESETS_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESETS_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{vol.Optional(v, default=0): vol.Coerce(float) for (k, v) in CONF_PRESETS.items()}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA = (
|
||||
vol.Schema( # pylint: disable=invalid-name # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(v, default=0): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_WITH_AC.items()
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
STEP_WINDOW_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_WINDOW_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[BINARY_SENSOR_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
),
|
||||
vol.Required(CONF_USE_WINDOW_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_WINDOW_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_WINDOW_DELAY, default=30): cv.positive_int,
|
||||
vol.Optional(CONF_WINDOW_AUTO_OPEN_THRESHOLD, default=3): vol.Coerce(float),
|
||||
vol.Optional(CONF_WINDOW_AUTO_CLOSE_THRESHOLD, default=0): vol.Coerce(float),
|
||||
vol.Optional(CONF_WINDOW_AUTO_MAX_DURATION, default=30): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_WINDOW_WO_AUTO_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_WINDOW_DELAY, default=30): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_MOTION_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_MOTION_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[BINARY_SENSOR_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
),
|
||||
vol.Required(CONF_USE_MOTION_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_MOTION_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_MOTION_DELAY, default=30): cv.positive_int,
|
||||
vol.Optional(CONF_MOTION_OFF_DELAY, default=300): cv.positive_int,
|
||||
vol.Optional(CONF_MOTION_PRESET, default="comfort"): vol.In(
|
||||
CONF_PRESETS_SELECTIONABLE
|
||||
),
|
||||
vol.Optional(CONF_NO_MOTION_PRESET, default="eco"): vol.In(
|
||||
CONF_PRESETS_SELECTIONABLE
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_MAX_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_PRESET_POWER, default="13"): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_USE_POWER_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(v, default=17): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_AWAY.items()
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESENCE_WITH_AC_DATA_SCHEMA = { # pylint: disable=invalid-name
|
||||
vol.Optional(v, default=17): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_AWAY_WITH_AC.items()
|
||||
}
|
||||
|
||||
STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_PRESENCE_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[
|
||||
PERSON_DOMAIN,
|
||||
BINARY_SENSOR_DOMAIN,
|
||||
INPUT_BOOLEAN_DOMAIN,
|
||||
]
|
||||
),
|
||||
),
|
||||
vol.Required(CONF_USE_PRESENCE_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_ADVANCED_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_MINIMAL_ACTIVATION_DELAY, default=10): cv.positive_int,
|
||||
vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int,
|
||||
vol.Required(
|
||||
CONF_SECURITY_MIN_ON_PERCENT,
|
||||
default=DEFAULT_SECURITY_MIN_ON_PERCENT,
|
||||
): vol.Coerce(float),
|
||||
vol.Required(
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||
default=DEFAULT_SECURITY_DEFAULT_ON_PERCENT,
|
||||
): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
STEP_ADVANCED_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_USE_ADVANCED_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
@@ -35,7 +35,12 @@ HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
|
||||
|
||||
DOMAIN = "versatile_thermostat"
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.CLIMATE,
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.SENSOR,
|
||||
Platform.SELECT,
|
||||
]
|
||||
|
||||
CONF_HEATER = "heater_entity_id"
|
||||
CONF_HEATER_2 = "heater_entity2_id"
|
||||
@@ -66,6 +71,7 @@ CONF_SECURITY_DELAY_MIN = "security_delay_min"
|
||||
CONF_SECURITY_MIN_ON_PERCENT = "security_min_on_percent"
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT = "security_default_on_percent"
|
||||
CONF_THERMOSTAT_TYPE = "thermostat_type"
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG = "thermostat_central_config"
|
||||
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
|
||||
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
|
||||
CONF_THERMOSTAT_VALVE = "thermostat_over_valve"
|
||||
@@ -103,6 +109,17 @@ CONF_AUTO_FAN_MEDIUM = "auto_fan_medium"
|
||||
CONF_AUTO_FAN_HIGH = "auto_fan_high"
|
||||
CONF_AUTO_FAN_TURBO = "auto_fan_turbo"
|
||||
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG = "use_main_central_config"
|
||||
CONF_USE_TPI_CENTRAL_CONFIG = "use_tpi_central_config"
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG = "use_window_central_config"
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG = "use_motion_central_config"
|
||||
CONF_USE_POWER_CENTRAL_CONFIG = "use_power_central_config"
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG = "use_presence_central_config"
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG = "use_presets_central_config"
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG = "use_advanced_central_config"
|
||||
|
||||
CONF_USE_CENTRAL_MODE = "use_central_mode"
|
||||
|
||||
DEFAULT_SHORT_EMA_PARAMS = {
|
||||
"max_alpha": 0.5,
|
||||
# In sec
|
||||
@@ -224,6 +241,15 @@ ALL_CONF = (
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||
CONF_INVERSE_SWITCH,
|
||||
CONF_AUTO_FAN_MODE,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG,
|
||||
CONF_USE_CENTRAL_MODE,
|
||||
]
|
||||
+ CONF_PRESETS_VALUES
|
||||
+ CONF_PRESETS_AWAY_VALUES
|
||||
@@ -245,6 +271,7 @@ CONF_AUTO_REGULATION_MODES = [
|
||||
]
|
||||
|
||||
CONF_THERMOSTAT_TYPES = [
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_VALVE,
|
||||
@@ -276,6 +303,21 @@ ATTR_MEAN_POWER_CYCLE = "mean_cycle_power"
|
||||
AUTO_FAN_DTEMP_THRESHOLD = 2
|
||||
AUTO_FAN_DEACTIVATED_MODES = ["mute", "auto", "low"]
|
||||
|
||||
CENTRAL_CONFIG_NAME = "Central configuration"
|
||||
|
||||
CENTRAL_MODE_AUTO = "Auto"
|
||||
CENTRAL_MODE_STOPPED = "Stopped"
|
||||
CENTRAL_MODE_HEAT_ONLY = "Heat only"
|
||||
CENTRAL_MODE_COOL_ONLY = "Cool only"
|
||||
CENTRAL_MODE_FROST_PROTECTION = "Frost protection"
|
||||
CENTRAL_MODES = [
|
||||
CENTRAL_MODE_AUTO,
|
||||
CENTRAL_MODE_STOPPED,
|
||||
CENTRAL_MODE_HEAT_ONLY,
|
||||
CENTRAL_MODE_COOL_ONLY,
|
||||
CENTRAL_MODE_FROST_PROTECTION,
|
||||
]
|
||||
|
||||
|
||||
# A special regulation parameter suggested by @Maia here: https://github.com/jmcollin78/versatile_thermostat/discussions/154
|
||||
class RegulationParamSlow:
|
||||
@@ -363,6 +405,10 @@ class WindowOpenDetectionMethod(HomeAssistantError):
|
||||
"""Error to indicate there is an error in the window open detection method given."""
|
||||
|
||||
|
||||
class NoCentralConfig(HomeAssistantError):
|
||||
"""Error to indicate that we try to use a central configuration but no VTherm of type CENTRAL CONFIGURATION has been found"""
|
||||
|
||||
|
||||
class overrides: # pylint: disable=invalid-name
|
||||
"""An annotation to inform overrides"""
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"quality_scale": "silver",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"version": "4.3.0",
|
||||
"version": "5.2.2",
|
||||
"zeroconf": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class PropAlgorithm:
|
||||
) -> None:
|
||||
"""Initialisation of the Proportional Algorithm"""
|
||||
_LOGGER.debug(
|
||||
"Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d",
|
||||
"Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d", # pylint: disable=line-too-long
|
||||
function_type,
|
||||
tpi_coef_int,
|
||||
tpi_coef_ext,
|
||||
@@ -140,27 +140,27 @@ class PropAlgorithm:
|
||||
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
||||
|
||||
def set_security(self, default_on_percent: float):
|
||||
"""Set a default value for on_percent (used for security mode)"""
|
||||
"""Set a default value for on_percent (used for safety mode)"""
|
||||
self._security = True
|
||||
self._default_on_percent = default_on_percent
|
||||
self._calculate_internal()
|
||||
|
||||
def unset_security(self):
|
||||
"""Unset the security mode"""
|
||||
"""Unset the safety mode"""
|
||||
self._security = False
|
||||
self._calculate_internal()
|
||||
|
||||
@property
|
||||
def on_percent(self) -> float:
|
||||
"""Returns the percentage the heater must be ON
|
||||
In security mode this value is overriden with the _default_on_percent
|
||||
In safety mode this value is overriden with the _default_on_percent
|
||||
(1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
||||
return round(self._on_percent, 2)
|
||||
|
||||
@property
|
||||
def calculated_on_percent(self) -> float:
|
||||
"""Returns the calculated percentage the heater must be ON
|
||||
Calculated means NOT overriden even in security mode
|
||||
Calculated means NOT overriden even in safety mode
|
||||
(1 means the heater will be always on, 0 never on)""" # pylint: disable=line-too-long
|
||||
return round(self._calculated_on_percent, 2)
|
||||
|
||||
|
||||
134
custom_components/versatile_thermostat/select.py
Normal file
134
custom_components/versatile_thermostat/select.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
""" Implements the VersatileThermostat select component """
|
||||
import logging
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, CoreState, callback
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
|
||||
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICE_MANUFACTURER,
|
||||
CONF_NAME,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CENTRAL_MODE_AUTO,
|
||||
CENTRAL_MODES,
|
||||
overrides,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the VersatileThermostat selects with config flow."""
|
||||
_LOGGER.debug(
|
||||
"Calling async_setup_entry entry=%s, data=%s", entry.entry_id, entry.data
|
||||
)
|
||||
|
||||
unique_id = entry.entry_id
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
|
||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
return
|
||||
|
||||
entities = [
|
||||
CentralModeSelect(hass, unique_id, name, entry.data),
|
||||
]
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class CentralModeSelect(SelectEntity, RestoreEntity):
|
||||
"""Representation of a Energy sensor which exposes the energy"""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||
"""Initialize the energy sensor"""
|
||||
self._config_id = unique_id
|
||||
self._device_name = entry_infos.get(CONF_NAME)
|
||||
self._attr_name = "Central Mode"
|
||||
self._attr_unique_id = "central_mode"
|
||||
self._attr_options = CENTRAL_MODES
|
||||
self._attr_current_option = CENTRAL_MODE_AUTO
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
return "mdi:form-select"
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info."""
|
||||
return DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, self._config_id)},
|
||||
name=self._device_name,
|
||||
manufacturer=DEVICE_MANUFACTURER,
|
||||
model=DOMAIN,
|
||||
)
|
||||
|
||||
@overrides
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
|
||||
old_state = await self.async_get_last_state()
|
||||
_LOGGER.debug(
|
||||
"%s - Calling async_added_to_hass old_state is %s", self, old_state
|
||||
)
|
||||
if old_state is not None:
|
||||
self._attr_current_option = old_state.state
|
||||
|
||||
@callback
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
await self.notify_central_mode_change()
|
||||
|
||||
if self.hass.state == CoreState.running:
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
)
|
||||
|
||||
@overrides
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
old_option = self._attr_current_option
|
||||
|
||||
if option == old_option:
|
||||
return
|
||||
|
||||
if option in CENTRAL_MODES:
|
||||
self._attr_current_option = option
|
||||
await self.notify_central_mode_change(old_central_mode=old_option)
|
||||
|
||||
async def notify_central_mode_change(self, old_central_mode=None):
|
||||
"""Notify all VTherm that the central_mode have change"""
|
||||
# Update all VTherm states
|
||||
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
|
||||
for entity in component.entities:
|
||||
if isinstance(entity, BaseThermostat):
|
||||
_LOGGER.debug(
|
||||
"Changing the central_mode. We have find %s to update",
|
||||
entity.name,
|
||||
)
|
||||
await entity.check_central_mode(
|
||||
self._attr_current_option, old_central_mode
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
@@ -27,6 +27,7 @@ from .const import (
|
||||
CONF_THERMOSTAT_VALVE,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
THRESHOLD_WATT_KILO = 100
|
||||
@@ -46,6 +47,10 @@ async def async_setup_entry(
|
||||
|
||||
unique_id = entry.entry_id
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
|
||||
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
return
|
||||
|
||||
entities = [
|
||||
LastTemperatureSensor(hass, unique_id, name, entry.data),
|
||||
|
||||
@@ -77,8 +77,8 @@ set_preset_temperature:
|
||||
mode: slider
|
||||
|
||||
set_security:
|
||||
name: Set security
|
||||
description: Change the security parameters
|
||||
name: Set safety
|
||||
description: Change the safety parameters
|
||||
target:
|
||||
entity:
|
||||
integration: versatile_thermostat
|
||||
@@ -97,7 +97,7 @@ set_security:
|
||||
mode: box
|
||||
min_on_percent:
|
||||
name: Minimal on_percent
|
||||
description: Minimal heating percent value for security preset activation
|
||||
description: Minimal heating percent value for safety preset activation
|
||||
required: false
|
||||
advanced: false
|
||||
example: "0.5"
|
||||
@@ -110,8 +110,8 @@ set_security:
|
||||
unit_of_measurement: "%"
|
||||
mode: slider
|
||||
default_on_percent:
|
||||
name: on_percent used in security mode
|
||||
description: The default heating percent value in security preset
|
||||
name: on_percent used in safety mode
|
||||
description: The default heating percent value in safety preset
|
||||
required: false
|
||||
advanced: false
|
||||
example: "0.1"
|
||||
|
||||
@@ -4,21 +4,37 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Type of Versatile Thermostat",
|
||||
"data": {
|
||||
"thermostat_type": "Thermostat type"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config)",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
|
||||
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -71,8 +87,14 @@
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
"data": {
|
||||
"tpi_coef_int": "coef_int",
|
||||
"tpi_coef_ext": "coef_ext",
|
||||
"use_tpi_central_config": "Use central TPI configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Coefficient to use for internal temperature delta",
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta"
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta",
|
||||
"use_tpi_central_config": "Check to use the central TPI configuration. Uncheck to use a specific TPI configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
@@ -85,7 +107,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
@@ -94,57 +117,69 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Window management",
|
||||
"description": "Open window management.\nLeave corresponding entity_id empty if not used\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Window sensor entity id",
|
||||
"window_delay": "Window sensor delay (seconds)",
|
||||
"window_auto_open_threshold": "Temperature decrease threshold for automatic window open detection (in °/hours)",
|
||||
"window_auto_close_threshold": "Temperature increase threshold for end of automatic detection (in °/hours)",
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)",
|
||||
"use_window_central_config": "Use central window configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used and to use the automatic detection",
|
||||
"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 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"
|
||||
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used",
|
||||
"use_window_central_config": "Check to use the central window configuration. Uncheck to use a specific window configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"title": "Motion management",
|
||||
"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",
|
||||
"description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Motion sensor entity id",
|
||||
"motion_delay": "Activation delay",
|
||||
"motion_off_delay": "Deactivation delay",
|
||||
"motion_preset": "Motion preset",
|
||||
"no_motion_preset": "No motion preset"
|
||||
"no_motion_preset": "No motion preset",
|
||||
"use_motion_central_config": "Use central motion configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "The entity id of the motion sensor",
|
||||
"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"
|
||||
"no_motion_preset": "Preset to use when no motion is detected",
|
||||
"use_motion_central_config": "Check to use the central motion configuration. Uncheck to use a specific motion configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Power management",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).\nLeave corresponding entity_id empty if not used.",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Power",
|
||||
"max_power_sensor_entity_id": "Max power",
|
||||
"power_temp": "Shedding temperature",
|
||||
"use_power_central_config": "Use central power configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Power sensor entity id",
|
||||
"max_power_sensor_entity_id": "Max power sensor entity id",
|
||||
"power_temp": "Temperature for Power shedding"
|
||||
"power_temp": "Temperature for Power shedding",
|
||||
"use_power_central_config": "Check to use the central power configuration. Uncheck to use a specific power configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco preset",
|
||||
@@ -153,7 +188,8 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Boost pres et in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
@@ -163,7 +199,8 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -171,22 +208,25 @@
|
||||
"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 to enable security mode",
|
||||
"security_default_on_percent": "Power percent to use in security mode"
|
||||
"security_delay_min": "Safety delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable safety mode",
|
||||
"security_default_on_percent": "Power percent to use in safety mode",
|
||||
"use_advanced_central_config": "Use central advanced configuration"
|
||||
},
|
||||
"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 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 preset"
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state",
|
||||
"security_min_on_percent": "Minimal heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset",
|
||||
"security_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset",
|
||||
"use_advanced_central_config": "Check to use the central advanced configuration. Uncheck to use a specific advanced configuration for this VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Unexpected error",
|
||||
"unknown_entity": "Unknown entity id",
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both",
|
||||
"no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
@@ -196,25 +236,41 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"title": "Type - {name}",
|
||||
"data": {
|
||||
"thermostat_type": "Thermostat type"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Main - {name}",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"temperature_sensor_entity_id": "Room temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"device_power": "Device power (kW)",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config)",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
|
||||
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific configuration for this VTherm",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entities",
|
||||
"title": "Entities - {name}",
|
||||
"description": "Linked entities attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "1st heater switch",
|
||||
@@ -260,15 +316,21 @@
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"title": "TPI - {name}",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
"data": {
|
||||
"tpi_coef_int": "coef_int",
|
||||
"tpi_coef_ext": "coef_ext",
|
||||
"use_tpi_central_config": "Use central TPI configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Coefficient to use for internal temperature delta",
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta"
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta",
|
||||
"use_tpi_central_config": "Check to use the central TPI configuration. Uncheck to use a specific TPI configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"title": "Presets - {name}",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
@@ -277,7 +339,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
@@ -286,88 +349,116 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Window management",
|
||||
"description": "Open window management.\nLeave corresponding entity_id empty if not used\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"title": "Window - {name}",
|
||||
"description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Window sensor entity id",
|
||||
"window_delay": "Window sensor delay (seconds)",
|
||||
"window_auto_open_threshold": "Temperature decrease threshold for automatic window open detection (in °/hours)",
|
||||
"window_auto_close_threshold": "Temperature increase threshold for end of automatic detection (in °/hours)",
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)",
|
||||
"use_window_central_config": "Use central window configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used and to use the automatic detection",
|
||||
"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 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"
|
||||
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used",
|
||||
"use_window_central_config": "Check to use the central window configuration. Uncheck to use a specific window configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"title": "Motion - {name}",
|
||||
"description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Motion sensor entity id",
|
||||
"motion_delay": "Activation delay",
|
||||
"motion_off_delay": "Deactivation delay",
|
||||
"motion_preset": "Motion preset",
|
||||
"no_motion_preset": "No motion preset"
|
||||
"no_motion_preset": "No motion preset",
|
||||
"use_motion_central_config": "Use central motion configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "The entity id of the motion sensor",
|
||||
"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"
|
||||
"no_motion_preset": "Preset to use when no motion is detected",
|
||||
"use_motion_central_config": "Check to use the central motion configuration. Uncheck to use a specific motion configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Power management",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).\nLeave corresponding entity_id empty if not used.",
|
||||
"title": "Power - {name}",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Power",
|
||||
"max_power_sensor_entity_id": "Max power",
|
||||
"power_temp": "Shedding temperature",
|
||||
"use_power_central_config": "Use central power configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Power sensor entity id",
|
||||
"max_power_sensor_entity_id": "Max power sensor entity id",
|
||||
"power_temp": "Temperature for Power shedding"
|
||||
"power_temp": "Temperature for Power shedding",
|
||||
"use_power_central_config": "Check to use the central power configuration. Uncheck to use a specific power configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"title": "Presence - {name}",
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco away preset",
|
||||
"comfort_away_temp": "Comfort away preset",
|
||||
"boost_away_temp": "Boost away preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco away preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort away preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost away preset in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
},
|
||||
"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"
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.",
|
||||
"title": "Advanced - {name}",
|
||||
"description": "Advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Minimal activation delay",
|
||||
"security_delay_min": "Security delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable security mode",
|
||||
"security_default_on_percent": "Power percent to use in security mode"
|
||||
"security_delay_min": "Safety delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable safety mode",
|
||||
"security_default_on_percent": "Power percent to use in safety mode",
|
||||
"use_advanced_central_config": "Use central advanced configuration"
|
||||
},
|
||||
"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 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 preset"
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state",
|
||||
"security_min_on_percent": "Minimal heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset",
|
||||
"security_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset",
|
||||
"use_advanced_central_config": "Check to use the central advanced configuration. Uncheck to use a specific advanced configuration for this VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Unexpected error",
|
||||
"unknown_entity": "Unknown entity id",
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both",
|
||||
"no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
@@ -376,6 +467,7 @@
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_central_config": "Central configuration",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over a climate",
|
||||
"thermostat_over_valve": "Thermostat over a valve"
|
||||
@@ -408,7 +500,7 @@
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"power": "Shedding",
|
||||
"security": "Security",
|
||||
"security": "Safety",
|
||||
"none": "Manual"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,11 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
|
||||
async def _send_regulated_temperature(self, force=False):
|
||||
"""Sends the regulated temperature to all underlying"""
|
||||
|
||||
if self.hvac_mode == HVACMode.OFF:
|
||||
_LOGGER.debug("%s - don't send regulated temperature cause VTherm is off ")
|
||||
return
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - Calling ThermostatClimate._send_regulated_temperature force=%s",
|
||||
self,
|
||||
@@ -234,45 +239,45 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
await self.async_set_fan_mode(self._auto_deactivated_fan_mode)
|
||||
|
||||
@overrides
|
||||
def post_init(self, entry_infos):
|
||||
def post_init(self, config_entry):
|
||||
"""Initialize the Thermostat"""
|
||||
|
||||
super().post_init(entry_infos)
|
||||
super().post_init(config_entry)
|
||||
for climate in [
|
||||
CONF_CLIMATE,
|
||||
CONF_CLIMATE_2,
|
||||
CONF_CLIMATE_3,
|
||||
CONF_CLIMATE_4,
|
||||
]:
|
||||
if entry_infos.get(climate):
|
||||
if config_entry.get(climate):
|
||||
self._underlyings.append(
|
||||
UnderlyingClimate(
|
||||
hass=self._hass,
|
||||
thermostat=self,
|
||||
climate_entity_id=entry_infos.get(climate),
|
||||
climate_entity_id=config_entry.get(climate),
|
||||
)
|
||||
)
|
||||
|
||||
self.choose_auto_regulation_mode(
|
||||
entry_infos.get(CONF_AUTO_REGULATION_MODE)
|
||||
if entry_infos.get(CONF_AUTO_REGULATION_MODE) is not None
|
||||
config_entry.get(CONF_AUTO_REGULATION_MODE)
|
||||
if config_entry.get(CONF_AUTO_REGULATION_MODE) is not None
|
||||
else CONF_AUTO_REGULATION_NONE
|
||||
)
|
||||
|
||||
self._auto_regulation_dtemp = (
|
||||
entry_infos.get(CONF_AUTO_REGULATION_DTEMP)
|
||||
if entry_infos.get(CONF_AUTO_REGULATION_DTEMP) is not None
|
||||
config_entry.get(CONF_AUTO_REGULATION_DTEMP)
|
||||
if config_entry.get(CONF_AUTO_REGULATION_DTEMP) is not None
|
||||
else 0.5
|
||||
)
|
||||
self._auto_regulation_period_min = (
|
||||
entry_infos.get(CONF_AUTO_REGULATION_PERIOD_MIN)
|
||||
if entry_infos.get(CONF_AUTO_REGULATION_PERIOD_MIN) is not None
|
||||
config_entry.get(CONF_AUTO_REGULATION_PERIOD_MIN)
|
||||
if config_entry.get(CONF_AUTO_REGULATION_PERIOD_MIN) is not None
|
||||
else 5
|
||||
)
|
||||
|
||||
self._auto_fan_mode = (
|
||||
entry_infos.get(CONF_AUTO_FAN_MODE)
|
||||
if entry_infos.get(CONF_AUTO_FAN_MODE) is not None
|
||||
config_entry.get(CONF_AUTO_FAN_MODE)
|
||||
if config_entry.get(CONF_AUTO_FAN_MODE) is not None
|
||||
else CONF_AUTO_FAN_NONE
|
||||
)
|
||||
|
||||
@@ -596,8 +601,9 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
# new_hvac_mode = HVACMode.OFF
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - Underlying climate changed. Event.new_hvac_mode is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
|
||||
"%s - Underlying climate %s changed. Event.new_hvac_mode is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
|
||||
self,
|
||||
new_state.entity_id,
|
||||
new_hvac_mode,
|
||||
self._hvac_mode,
|
||||
new_hvac_action,
|
||||
@@ -653,7 +659,7 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
)
|
||||
changes = True
|
||||
|
||||
# Issue #120 - Some TRV are chaning target temperature a very long time (6 sec) after the change.
|
||||
# Issue #120 - Some TRV are changing target temperature a very long time (6 sec) after the change.
|
||||
# In that case a loop is possible if a user change multiple times during this 6 sec.
|
||||
if new_state_date_updated and self._last_change_time:
|
||||
delta = (new_state_date_updated - self._last_change_time).total_seconds()
|
||||
@@ -679,12 +685,31 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
]
|
||||
and self._hvac_mode != new_hvac_mode
|
||||
):
|
||||
changes = True
|
||||
self._hvac_mode = new_hvac_mode
|
||||
# Update all underlyings state
|
||||
# Issue #334 - if all underlyings are not aligned with the same hvac_mode don't change the underlying and wait they are aligned
|
||||
if self.is_over_climate:
|
||||
for under in self._underlyings:
|
||||
if (
|
||||
under.entity_id != new_state.entity_id
|
||||
and under.hvac_mode != self._hvac_mode
|
||||
):
|
||||
_LOGGER.info(
|
||||
"%s - the underlying's hvac_mode %s is not aligned with VTherm hvac_mode %s. So we don't diffuse the change to all other underlyings to avoid loops",
|
||||
under,
|
||||
under.hvac_mode,
|
||||
self._hvac_mode,
|
||||
)
|
||||
return
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s - All underlyings have the same hvac_mode, so VTherm will send the new hvac mode %s",
|
||||
self,
|
||||
new_hvac_mode,
|
||||
)
|
||||
for under in self._underlyings:
|
||||
await under.set_hvac_mode(new_hvac_mode)
|
||||
changes = True
|
||||
self._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:
|
||||
|
||||
@@ -48,9 +48,9 @@ class ThermostatOverSwitch(BaseThermostat):
|
||||
)
|
||||
|
||||
# useless for now
|
||||
# def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||
# def __init__(self, hass: HomeAssistant, unique_id, name, config_entry) -> None:
|
||||
# """Initialize the thermostat over switch."""
|
||||
# super().__init__(hass, unique_id, name, entry_infos)
|
||||
# super().__init__(hass, unique_id, name, config_entry)
|
||||
_is_inversed: bool = None
|
||||
|
||||
@property
|
||||
@@ -72,10 +72,10 @@ class ThermostatOverSwitch(BaseThermostat):
|
||||
return None
|
||||
|
||||
@overrides
|
||||
def post_init(self, entry_infos):
|
||||
def post_init(self, config_entry):
|
||||
"""Initialize the Thermostat"""
|
||||
|
||||
super().post_init(entry_infos)
|
||||
super().post_init(config_entry)
|
||||
|
||||
self._prop_algorithm = PropAlgorithm(
|
||||
self._proportional_function,
|
||||
@@ -85,13 +85,13 @@ class ThermostatOverSwitch(BaseThermostat):
|
||||
self._minimal_activation_delay,
|
||||
)
|
||||
|
||||
lst_switches = [entry_infos.get(CONF_HEATER)]
|
||||
if entry_infos.get(CONF_HEATER_2):
|
||||
lst_switches.append(entry_infos.get(CONF_HEATER_2))
|
||||
if entry_infos.get(CONF_HEATER_3):
|
||||
lst_switches.append(entry_infos.get(CONF_HEATER_3))
|
||||
if entry_infos.get(CONF_HEATER_4):
|
||||
lst_switches.append(entry_infos.get(CONF_HEATER_4))
|
||||
lst_switches = [config_entry.get(CONF_HEATER)]
|
||||
if config_entry.get(CONF_HEATER_2):
|
||||
lst_switches.append(config_entry.get(CONF_HEATER_2))
|
||||
if config_entry.get(CONF_HEATER_3):
|
||||
lst_switches.append(config_entry.get(CONF_HEATER_3))
|
||||
if config_entry.get(CONF_HEATER_4):
|
||||
lst_switches.append(config_entry.get(CONF_HEATER_4))
|
||||
|
||||
delta_cycle = self._cycle_min * 60 / len(lst_switches)
|
||||
for idx, switch in enumerate(lst_switches):
|
||||
@@ -104,7 +104,7 @@ class ThermostatOverSwitch(BaseThermostat):
|
||||
)
|
||||
)
|
||||
|
||||
self._is_inversed = entry_infos.get(CONF_INVERSE_SWITCH) is True
|
||||
self._is_inversed = config_entry.get(CONF_INVERSE_SWITCH) is True
|
||||
self._should_relaunch_control_heating = False
|
||||
|
||||
@overrides
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.helpers.event import async_track_state_change_event, async_track_time_interval
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_state_change_event,
|
||||
async_track_time_interval,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components.climate import HVACMode
|
||||
|
||||
@@ -16,39 +19,53 @@ from .underlyings import UnderlyingValve
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ThermostatOverValve(BaseThermostat):
|
||||
"""Representation of a class for a Versatile Thermostat over a Valve"""
|
||||
|
||||
_entity_component_unrecorded_attributes = BaseThermostat._entity_component_unrecorded_attributes.union(frozenset(
|
||||
{
|
||||
"is_over_valve", "underlying_valve_0", "underlying_valve_1",
|
||||
"underlying_valve_2", "underlying_valve_3", "on_time_sec", "off_time_sec",
|
||||
"cycle_min", "function", "tpi_coef_int", "tpi_coef_ext"
|
||||
}))
|
||||
_entity_component_unrecorded_attributes = (
|
||||
BaseThermostat._entity_component_unrecorded_attributes.union(
|
||||
frozenset(
|
||||
{
|
||||
"is_over_valve",
|
||||
"underlying_valve_0",
|
||||
"underlying_valve_1",
|
||||
"underlying_valve_2",
|
||||
"underlying_valve_3",
|
||||
"on_time_sec",
|
||||
"off_time_sec",
|
||||
"cycle_min",
|
||||
"function",
|
||||
"tpi_coef_int",
|
||||
"tpi_coef_ext",
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Useless for now
|
||||
# def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||
# def __init__(self, hass: HomeAssistant, unique_id, name, config_entry) -> None:
|
||||
# """Initialize the thermostat over switch."""
|
||||
# super().__init__(hass, unique_id, name, entry_infos)
|
||||
# super().__init__(hass, unique_id, name, config_entry)
|
||||
|
||||
@property
|
||||
def is_over_valve(self) -> bool:
|
||||
""" True if the Thermostat is over_valve"""
|
||||
"""True if the Thermostat is over_valve"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def valve_open_percent(self) -> int:
|
||||
""" Gives the percentage of valve needed"""
|
||||
"""Gives the percentage of valve needed"""
|
||||
if self._hvac_mode == HVACMode.OFF:
|
||||
return 0
|
||||
else:
|
||||
return round(max(0, min(self.proportional_algorithm.on_percent, 1)) * 100)
|
||||
|
||||
@overrides
|
||||
def post_init(self, entry_infos):
|
||||
""" Initialize the Thermostat"""
|
||||
def post_init(self, config_entry):
|
||||
"""Initialize the Thermostat"""
|
||||
|
||||
super().post_init(entry_infos)
|
||||
super().post_init(config_entry)
|
||||
self._prop_algorithm = PropAlgorithm(
|
||||
self._proportional_function,
|
||||
self._tpi_coef_int,
|
||||
@@ -57,21 +74,17 @@ class ThermostatOverValve(BaseThermostat):
|
||||
self._minimal_activation_delay,
|
||||
)
|
||||
|
||||
lst_valves = [entry_infos.get(CONF_VALVE)]
|
||||
if entry_infos.get(CONF_VALVE_2):
|
||||
lst_valves.append(entry_infos.get(CONF_VALVE_2))
|
||||
if entry_infos.get(CONF_VALVE_3):
|
||||
lst_valves.append(entry_infos.get(CONF_VALVE_3))
|
||||
if entry_infos.get(CONF_VALVE_4):
|
||||
lst_valves.append(entry_infos.get(CONF_VALVE_4))
|
||||
lst_valves = [config_entry.get(CONF_VALVE)]
|
||||
if config_entry.get(CONF_VALVE_2):
|
||||
lst_valves.append(config_entry.get(CONF_VALVE_2))
|
||||
if config_entry.get(CONF_VALVE_3):
|
||||
lst_valves.append(config_entry.get(CONF_VALVE_3))
|
||||
if config_entry.get(CONF_VALVE_4):
|
||||
lst_valves.append(config_entry.get(CONF_VALVE_4))
|
||||
|
||||
for _, valve in enumerate(lst_valves):
|
||||
self._underlyings.append(
|
||||
UnderlyingValve(
|
||||
hass=self._hass,
|
||||
thermostat=self,
|
||||
valve_entity_id=valve
|
||||
)
|
||||
UnderlyingValve(hass=self._hass, thermostat=self, valve_entity_id=valve)
|
||||
)
|
||||
|
||||
self._should_relaunch_control_heating = False
|
||||
@@ -89,7 +102,7 @@ class ThermostatOverValve(BaseThermostat):
|
||||
async_track_state_change_event(
|
||||
self.hass, [valve.entity_id], self._async_valve_changed
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Start the control_heating
|
||||
# starts a cycle
|
||||
@@ -107,29 +120,34 @@ class ThermostatOverValve(BaseThermostat):
|
||||
This method just log the change. It changes nothing to avoid loops.
|
||||
"""
|
||||
new_state = event.data.get("new_state")
|
||||
_LOGGER.debug("%s - _async_valve_changed new_state is %s", self, new_state.state)
|
||||
_LOGGER.debug(
|
||||
"%s - _async_valve_changed new_state is %s", self, new_state.state
|
||||
)
|
||||
|
||||
@overrides
|
||||
def update_custom_attributes(self):
|
||||
""" Custom attributes """
|
||||
"""Custom attributes"""
|
||||
super().update_custom_attributes()
|
||||
self._attr_extra_state_attributes["valve_open_percent"] = self.valve_open_percent
|
||||
self._attr_extra_state_attributes[
|
||||
"valve_open_percent"
|
||||
] = self.valve_open_percent
|
||||
self._attr_extra_state_attributes["is_over_valve"] = self.is_over_valve
|
||||
self._attr_extra_state_attributes["underlying_valve_0"] = (
|
||||
self._underlyings[0].entity_id)
|
||||
self._attr_extra_state_attributes["underlying_valve_0"] = self._underlyings[
|
||||
0
|
||||
].entity_id
|
||||
self._attr_extra_state_attributes["underlying_valve_1"] = (
|
||||
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None
|
||||
)
|
||||
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None
|
||||
)
|
||||
self._attr_extra_state_attributes["underlying_valve_2"] = (
|
||||
self._underlyings[2].entity_id if len(self._underlyings) > 2 else None
|
||||
)
|
||||
self._underlyings[2].entity_id if len(self._underlyings) > 2 else None
|
||||
)
|
||||
self._attr_extra_state_attributes["underlying_valve_3"] = (
|
||||
self._underlyings[3].entity_id if len(self._underlyings) > 3 else None
|
||||
)
|
||||
self._underlyings[3].entity_id if len(self._underlyings) > 3 else None
|
||||
)
|
||||
|
||||
self._attr_extra_state_attributes[
|
||||
"on_percent"
|
||||
] = self._prop_algorithm.on_percent
|
||||
"on_percent"
|
||||
] = self._prop_algorithm.on_percent
|
||||
self._attr_extra_state_attributes[
|
||||
"on_time_sec"
|
||||
] = self._prop_algorithm.on_time_sec
|
||||
@@ -162,9 +180,7 @@ class ThermostatOverValve(BaseThermostat):
|
||||
)
|
||||
|
||||
for under in self._underlyings:
|
||||
under.set_valve_open_percent(
|
||||
self._prop_algorithm.on_percent
|
||||
)
|
||||
under.set_valve_open_percent()
|
||||
|
||||
self.update_custom_attributes()
|
||||
self.async_write_ha_state()
|
||||
@@ -185,4 +201,4 @@ class ThermostatOverValve(BaseThermostat):
|
||||
self,
|
||||
added_energy,
|
||||
self._total_energy,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,21 +4,37 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Type of Versatile Thermostat",
|
||||
"data": {
|
||||
"thermostat_type": "Thermostat type"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config)",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
|
||||
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -71,8 +87,14 @@
|
||||
"title": "TPI",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
"data": {
|
||||
"tpi_coef_int": "coef_int",
|
||||
"tpi_coef_ext": "coef_ext",
|
||||
"use_tpi_central_config": "Use central TPI configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Coefficient to use for internal temperature delta",
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta"
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta",
|
||||
"use_tpi_central_config": "Check to use the central TPI configuration. Uncheck to use a specific TPI configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
@@ -85,7 +107,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
@@ -94,57 +117,69 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Window management",
|
||||
"description": "Open window management.\nLeave corresponding entity_id empty if not used\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Window sensor entity id",
|
||||
"window_delay": "Window sensor delay (seconds)",
|
||||
"window_auto_open_threshold": "Temperature decrease threshold for automatic window open detection (in °/hours)",
|
||||
"window_auto_close_threshold": "Temperature increase threshold for end of automatic detection (in °/hours)",
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)",
|
||||
"use_window_central_config": "Use central window configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used and to use the automatic detection",
|
||||
"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 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"
|
||||
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used",
|
||||
"use_window_central_config": "Check to use the central window configuration. Uncheck to use a specific window configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"title": "Motion management",
|
||||
"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",
|
||||
"description": "Motion sensor management. Preset can switch automatically depending on motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Motion sensor entity id",
|
||||
"motion_delay": "Activation delay",
|
||||
"motion_off_delay": "Deactivation delay",
|
||||
"motion_preset": "Motion preset",
|
||||
"no_motion_preset": "No motion preset"
|
||||
"no_motion_preset": "No motion preset",
|
||||
"use_motion_central_config": "Use central motion configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "The entity id of the motion sensor",
|
||||
"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"
|
||||
"no_motion_preset": "Preset to use when no motion is detected",
|
||||
"use_motion_central_config": "Check to use the central motion configuration. Uncheck to use a specific motion configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Power management",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).\nLeave corresponding entity_id empty if not used.",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Power",
|
||||
"max_power_sensor_entity_id": "Max power",
|
||||
"power_temp": "Shedding temperature",
|
||||
"use_power_central_config": "Use central power configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Power sensor entity id",
|
||||
"max_power_sensor_entity_id": "Max power sensor entity id",
|
||||
"power_temp": "Temperature for Power shedding"
|
||||
"power_temp": "Temperature for Power shedding",
|
||||
"use_power_central_config": "Check to use the central power configuration. Uncheck to use a specific power configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco preset",
|
||||
@@ -153,7 +188,8 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Boost pres et in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
@@ -163,7 +199,8 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -171,22 +208,25 @@
|
||||
"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 to enable security mode",
|
||||
"security_default_on_percent": "Power percent to use in security mode"
|
||||
"security_delay_min": "Safety delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable safety mode",
|
||||
"security_default_on_percent": "Power percent to use in safety mode",
|
||||
"use_advanced_central_config": "Use central advanced configuration"
|
||||
},
|
||||
"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 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 preset"
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state",
|
||||
"security_min_on_percent": "Minimal heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset",
|
||||
"security_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset",
|
||||
"use_advanced_central_config": "Check to use the central advanced configuration. Uncheck to use a specific advanced configuration for this VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Unexpected error",
|
||||
"unknown_entity": "Unknown entity id",
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both",
|
||||
"no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
@@ -196,25 +236,41 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"title": "Type - {name}",
|
||||
"data": {
|
||||
"thermostat_type": "Thermostat type"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Main - {name}",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"temperature_sensor_entity_id": "Room temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"device_power": "Device power (kW)",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config)",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
|
||||
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific configuration for this VTherm",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entities",
|
||||
"title": "Entities - {name}",
|
||||
"description": "Linked entities attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "1st heater switch",
|
||||
@@ -260,15 +316,21 @@
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"title": "TPI - {name}",
|
||||
"description": "Time Proportional Integral attributes",
|
||||
"data": {
|
||||
"tpi_coef_int": "coef_int",
|
||||
"tpi_coef_ext": "coef_ext",
|
||||
"use_tpi_central_config": "Use central TPI configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Coefficient to use for internal temperature delta",
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta"
|
||||
"tpi_coef_ext": "Coefficient to use for external temperature delta",
|
||||
"use_tpi_central_config": "Check to use the central TPI configuration. Uncheck to use a specific TPI configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"title": "Presets - {name}",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
@@ -277,7 +339,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
@@ -286,88 +349,116 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Window management",
|
||||
"description": "Open window management.\nLeave corresponding entity_id empty if not used\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"title": "Window - {name}",
|
||||
"description": "Open window management.\nYou can also configure automatic window open detection based on temperature decrease",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Window sensor entity id",
|
||||
"window_delay": "Window sensor delay (seconds)",
|
||||
"window_auto_open_threshold": "Temperature decrease threshold for automatic window open detection (in °/hours)",
|
||||
"window_auto_close_threshold": "Temperature increase threshold for end of automatic detection (in °/hours)",
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)"
|
||||
"window_auto_max_duration": "Maximum duration of automatic window open detection (in min)",
|
||||
"use_window_central_config": "Use central window configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used",
|
||||
"window_sensor_entity_id": "Leave empty if no window sensor should be used and to use the automatic detection",
|
||||
"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 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"
|
||||
"window_auto_max_duration": "Recommended value: 60 (one hour). Leave empty if automatic window open detection is not used",
|
||||
"use_window_central_config": "Check to use the central window configuration. Uncheck to use a specific window configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"title": "Motion - {name}",
|
||||
"description": "Motion management. Preset can switch automatically depending of a motion detection\nmotion_preset and no_motion_preset should be set to the corresponding preset name",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Motion sensor entity id",
|
||||
"motion_delay": "Activation delay",
|
||||
"motion_off_delay": "Deactivation delay",
|
||||
"motion_preset": "Motion preset",
|
||||
"no_motion_preset": "No motion preset"
|
||||
"no_motion_preset": "No motion preset",
|
||||
"use_motion_central_config": "Use central motion configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "The entity id of the motion sensor",
|
||||
"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"
|
||||
"no_motion_preset": "Preset to use when no motion is detected",
|
||||
"use_motion_central_config": "Check to use the central motion configuration. Uncheck to use a specific motion configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Power management",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).\nLeave corresponding entity_id empty if not used.",
|
||||
"title": "Power - {name}",
|
||||
"description": "Power management attributes.\nGives the power and max power sensor of your home.\nThen specify the power consumption of the heater when on.\nAll sensors and device power should have the same unit (kW or W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Power",
|
||||
"max_power_sensor_entity_id": "Max power",
|
||||
"power_temp": "Shedding temperature",
|
||||
"use_power_central_config": "Use central power configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Power sensor entity id",
|
||||
"max_power_sensor_entity_id": "Max power sensor entity id",
|
||||
"power_temp": "Temperature for Power shedding"
|
||||
"power_temp": "Temperature for Power shedding",
|
||||
"use_power_central_config": "Check to use the central power configuration. Uncheck to use a specific power configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"title": "Presence - {name}",
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco away preset",
|
||||
"comfort_away_temp": "Comfort away preset",
|
||||
"boost_away_temp": "Boost away preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco away preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort away preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost away preset in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
},
|
||||
"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"
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.",
|
||||
"title": "Advanced - {name}",
|
||||
"description": "Advanced parameters. Leave the default values if you don't know what you are doing.\nThese parameters can lead to very poor temperature control or bad power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Minimal activation delay",
|
||||
"security_delay_min": "Security delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable security mode",
|
||||
"security_default_on_percent": "Power percent to use in security mode"
|
||||
"security_delay_min": "Safety delay (in minutes)",
|
||||
"security_min_on_percent": "Minimal power percent to enable safety mode",
|
||||
"security_default_on_percent": "Power percent to use in safety mode",
|
||||
"use_advanced_central_config": "Use central advanced configuration"
|
||||
},
|
||||
"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 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 preset"
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature measurements. Above this delay the thermostat will turn to a safety off state",
|
||||
"security_min_on_percent": "Minimal heating percent value for safety preset activation. Below this amount of power percent the thermostat won't go into safety preset",
|
||||
"security_default_on_percent": "The default heating power percent value in safety preset. Set to 0 to switch off heater in safety preset",
|
||||
"use_advanced_central_config": "Check to use the central advanced configuration. Uncheck to use a specific advanced configuration for this VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Unexpected error",
|
||||
"unknown_entity": "Unknown entity id",
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both"
|
||||
"window_open_detection_method": "Only one window open detection method should be used. Use either window sensor or automatic detection through temperature threshold but not both",
|
||||
"no_central_config": "You cannot check 'use central configuration' because no central configuration was found. You need to create a Versatile Thermostat of type 'Central Configuration' to use it."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
@@ -376,6 +467,7 @@
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_central_config": "Central configuration",
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over a climate",
|
||||
"thermostat_over_valve": "Thermostat over a valve"
|
||||
@@ -408,7 +500,7 @@
|
||||
"preset_mode": {
|
||||
"state": {
|
||||
"power": "Shedding",
|
||||
"security": "Security",
|
||||
"security": "Safety",
|
||||
"none": "Manual"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Type du nouveau Versatile Thermostat",
|
||||
"data": {
|
||||
"thermostat_type": "Type de thermostat"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Un seul thermostat de type Configuration centrale est possible."
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Ajout d'un nouveau thermostat",
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
@@ -15,10 +24,17 @@
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise",
|
||||
"device_power": "Puissance de l'équipement",
|
||||
"use_central_mode": "Autoriser le controle par une entity centrale ('nécessite une config. centrale`)",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_main_central_config": "Utiliser la configuration centrale principale"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale",
|
||||
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure.",
|
||||
"use_main_central_config": "Cochez pour utiliser la configuration centrale principale. Décochez et saisissez les attributs pour utiliser une configuration spécifique principale"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -71,8 +87,14 @@
|
||||
"title": "TPI",
|
||||
"description": "Attributs de l'algo Time Proportional Integral",
|
||||
"data": {
|
||||
"tpi_coef_int": "coeff_int : Coefficient à utiliser pour le delta de température interne",
|
||||
"tpi_coef_ext": "coeff_ext : Coefficient à utiliser pour le delta de température externe"
|
||||
"tpi_coef_int": "coeff_int",
|
||||
"tpi_coef_ext": "coeff_ext",
|
||||
"use_tpi_central_config": "Utiliser la configuration TPI centrale"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Coefficient à utiliser pour le delta de température interne",
|
||||
"tpi_coef_ext": "Coefficient à utiliser pour le delta de température externe",
|
||||
"use_tpi_central_config": "Cochez pour utiliser la configuration TPI centrale. Décochez et saisissez les attributs pour utiliser une configuration TPI spécifique"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
@@ -85,7 +107,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Preset Boost en mode AC",
|
||||
"use_presets_central_config": "Utiliser la configuration des presets centrale"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Température en preset Eco",
|
||||
@@ -94,57 +117,69 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Température en preset Boost en mode AC",
|
||||
"use_presets_central_config": "Cochez pour utiliser la configuration des presets centrale. Décochez et saisissez les attributs pour utiliser une configuration des presets spécifique"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Gestion d'une ouverture",
|
||||
"description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'entity id vide si non utilisé.",
|
||||
"description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique.",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Détecteur d'ouverture (entity id)",
|
||||
"window_delay": "Délai avant extinction (secondes)",
|
||||
"window_auto_open_threshold": "Seuil haut de chute de température pour la détection automatique (en °/heure)",
|
||||
"window_auto_close_threshold": "Seuil bas de chute de température pour la fin de détection automatique (en °/heure)",
|
||||
"window_auto_max_duration": "Durée maximum d'une extinction automatique (en min)"
|
||||
"window_auto_max_duration": "Durée maximum d'une extinction automatique (en min)",
|
||||
"use_window_central_config": "Utiliser la configuration centrale des ouvertures"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Laissez vide si vous n'avez de détecteur",
|
||||
"window_sensor_entity_id": "Laissez vide si vous n'avez de détecteur et pour utiliser la détection automatique",
|
||||
"window_delay": "Le délai (en secondes) avant que le changement du détecteur soit pris en compte",
|
||||
"window_auto_open_threshold": "Valeur recommandée: entre 3 et 10. Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"window_auto_close_threshold": "Valeur recommandée: 0. Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"window_auto_max_duration": "Valeur recommandée: 60 (1 heure). Laissez vide si vous n'utilisez pas la détection automatique"
|
||||
"window_auto_max_duration": "Valeur recommandée: 60 (1 heure). Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"use_window_central_config": "Cochez pour utiliser la configuration centrale des ouvertures. Décochez et saisissez les attributs pour utiliser une configuration spécifique des ouvertures"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"title": "Gestion de la détection de mouvement",
|
||||
"description": "Le preset s'ajuste automatiquement si un mouvement est détecté\nLaissez l'entity id vide si non utilisé.\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.",
|
||||
"description": "Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Détecteur de mouvement",
|
||||
"motion_delay": "Délai d'activation",
|
||||
"motion_off_delay": "Délai de désactivation",
|
||||
"motion_preset": "Preset si mouvement",
|
||||
"no_motion_preset": "Preset si pas de mouvement"
|
||||
"no_motion_preset": "Preset sans mouvement",
|
||||
"use_motion_central_config": "Utiliser la condfiguration centrale du mouvement"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "Détecteur de mouvement entity id",
|
||||
"motion_sensor_entity_id": "Id d'entité du détecteur de mouvement",
|
||||
"motion_delay": "Délai avant activation lorsqu'un mouvement est détecté (secondss)",
|
||||
"motion_off_delai": "Délai avant désactivation lorsqu'aucun mouvement n'est détecté (secondes)",
|
||||
"motion_preset": "Preset à utiliser si mouvement détecté",
|
||||
"no_motion_preset": "Preset à utiliser si pas de mouvement détecté"
|
||||
"no_motion_preset": "Preset à utiliser si pas de mouvement détecté",
|
||||
"use_motion_central_config": "Cochez pour utiliser la configuration centrale du mouvement. Décochez et saisissez les attributs pour utiliser une configuration spécifique du mouvement"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Gestion de l'énergie",
|
||||
"title": "Gestion de la puissance",
|
||||
"description": "Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum.\nDonnez les entity id des capteurs qui mesurent la puissance totale et la puissance max autorisée.\nEnsuite donnez la puissance de l'équipement.\nTous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Capteur de puissance totale (entity id)",
|
||||
"max_power_sensor_entity_id": "Capteur de puissance Max (entity id)",
|
||||
"power_temp": "Température si délestaqe"
|
||||
"power_temp": "Température si délestaqe",
|
||||
"use_power_central_config": "Utiliser la configuration centrale de la puissance"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Entity id du capteur de puissance totale du logement",
|
||||
"max_power_sensor_entity_id": "Entity id du capteur de puissance Max autorisée avant délestage",
|
||||
"power_temp": "Température cible si délestaqe",
|
||||
"use_power_central_config": "Cochez pour utiliser la configuration centrale de la puissance. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la puissance"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Capteur de présence",
|
||||
"eco_away_temp": "preset Eco",
|
||||
@@ -153,17 +188,19 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "preset Boost en mode AC",
|
||||
"use_presence_central_config": "Utiliser la configuration centrale de la présence"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Capteur de présence entity id (true si quelqu'un est présent)",
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence",
|
||||
"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"
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC",
|
||||
"use_presence_central_config": "Cochez pour utiliser la configuration centrale de la présence. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la présence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -173,20 +210,23 @@
|
||||
"minimal_activation_delay": "Délai minimal d'activation",
|
||||
"security_delay_min": "Délai maximal entre 2 mesures de températures",
|
||||
"security_min_on_percent": "Pourcentage minimal de puissance",
|
||||
"security_default_on_percent": "Pourcentage de puissance a utiliser en mode securité"
|
||||
"security_default_on_percent": "Pourcentage de puissance a utiliser en mode securité",
|
||||
"use_advanced_central_config": "Utiliser la configuration centrale avancée"
|
||||
},
|
||||
"data_description": {
|
||||
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
|
||||
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position de sécurité",
|
||||
"security_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé",
|
||||
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité"
|
||||
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité",
|
||||
"use_advanced_central_config": "Cochez pour utiliser la configuration centrale avancée. Décochez et saisissez les attributs pour utiliser une configuration spécifique avancée"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Erreur inattendue",
|
||||
"unknown_entity": "entity id inconnu",
|
||||
"window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux."
|
||||
"window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux.",
|
||||
"no_central_config": "Vous ne pouvez pas cocher 'Utiliser la configuration centrale' car aucune configuration centrale n'a été trouvée. Vous devez créer un Versatile Thermostat de type 'Central Configuration' pour pouvoir l'utiliser."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Le device est déjà configuré"
|
||||
@@ -196,26 +236,41 @@
|
||||
"flow_title": "Versatile Thermostat configuration",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Ajout d'un nouveau thermostat",
|
||||
"title": "Type - {name}",
|
||||
"data": {
|
||||
"thermostat_type": "Type de thermostat"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Un seul thermostat de type Configuration centrale est possible."
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Attributs - {name}",
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
"name": "Nom",
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat",
|
||||
"thermostat_type": "Type de thermostat",
|
||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Température exterieure sensor entity id",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise",
|
||||
"device_power": "Puissance de l'équipement",
|
||||
"use_central_mode": "Autoriser le controle par une entity centrale ('nécessite une config. centrale`)",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_main_central_config": "Utiliser la configuration centrale"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale",
|
||||
"use_main_central_config": "Cochez pour utiliser la configuration centrale. Décochez et saisissez les attributs pour utiliser une configuration spécifique",
|
||||
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure. N'est pas utilisé si la configuration centrale est utilisée"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entité(s) liée(s)",
|
||||
"title": "Entités - {name}",
|
||||
"description": "Attributs de(s) l'entité(s) liée(s)",
|
||||
"data": {
|
||||
"heater_entity_id": "1er radiateur",
|
||||
@@ -261,7 +316,7 @@
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"title": "TPI - {name}",
|
||||
"description": "Attributs de l'algo Time Proportional Integral",
|
||||
"data": {
|
||||
"tpi_coef_int": "coeff_int : Coefficient à utiliser pour le delta de température interne",
|
||||
@@ -269,8 +324,8 @@
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"description": "Pour chaque preset, donnez la température cible (0 pour ignorer le preset)",
|
||||
"title": "Pre-réglages - {name}",
|
||||
"description": "Réglage des presets. Donnez la température cible (0 pour ignorer le preset)",
|
||||
"data": {
|
||||
"eco_temp": "Preset Eco",
|
||||
"comfort_temp": "Preset Comfort",
|
||||
@@ -278,7 +333,8 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Preset Boost en mode AC",
|
||||
"use_presets_central_config": "Utiliser la configuration centrale des presets"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Température en preset Eco",
|
||||
@@ -287,57 +343,69 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Température en preset Boost en mode AC",
|
||||
"use_presets_central_config": "Cochez pour utiliser la configuration centrale des presets. Décochez et saisissez les attributs pour utiliser une configuration des presets spécifique"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Gestion d'une ouverture",
|
||||
"description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'entity id vide si non utilisé.",
|
||||
"title": "Ouverture - {name}",
|
||||
"description": "Coupe le radiateur si l'ouverture est ouverte.\nLaissez l'id d'entité vide pour utiliser la détection automatique.",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "Détecteur d'ouverture (entity id)",
|
||||
"window_delay": "Délai avant extinction (secondes)",
|
||||
"window_auto_open_threshold": "Seuil haut de chute de température pour la détection automatique (en °/heure)",
|
||||
"window_auto_close_threshold": "Seuil bas de chute de température pour la fin de détection automatique (en °/heure)",
|
||||
"window_auto_max_duration": "Durée maximum d'une extinction automatique (en min)"
|
||||
"window_auto_max_duration": "Durée maximum d'une extinction automatique (en min)",
|
||||
"use_window_central_config": "Utiliser la configuration centrale des ouvertures"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Laissez vide si vous n'avez de détecteur",
|
||||
"window_sensor_entity_id": "Laissez vide si vous n'avez de détecteur et pour utiliser la détection automatique",
|
||||
"window_delay": "Le délai (en secondes) avant que le changement du détecteur soit pris en compte",
|
||||
"window_auto_open_threshold": "Valeur recommandée: entre 3 et 10. Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"window_auto_close_threshold": "Valeur recommandée: 0. Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"window_auto_max_duration": "Valeur recommandée: 60 (1 heure). Laissez vide si vous n'utilisez pas la détection automatique"
|
||||
"window_auto_max_duration": "Valeur recommandée: 60 (1 heure). Laissez vide si vous n'utilisez pas la détection automatique",
|
||||
"use_window_central_config": "Cochez pour utiliser la configuration centrale des ouvertures. Décochez et saisissez les attributs pour utiliser une configuration spécifique des ouvertures"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"title": "Gestion de la détection de mouvement",
|
||||
"description": "Le preset s'ajuste automatiquement si un mouvement est détecté\nLaissez l'entity id vide si non utilisé.\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.",
|
||||
"title": "Mouvement - {name}",
|
||||
"description": "Gestion du mouvement. Le preset s'ajuste automatiquement si un mouvement est détecté\n'Preset mouvement' et 'Preset sans mouvement' doivent être choisis avec les preset à utiliser.",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "Détecteur de mouvement",
|
||||
"motion_delay": "Délai d'activation",
|
||||
"motion_off_delay": "Délai de désactivation",
|
||||
"motion_preset": "Preset si mouvement",
|
||||
"no_motion_preset": "Preset si pas de mouvement"
|
||||
"no_motion_preset": "Preset sans mouvement",
|
||||
"use_motion_central_config": "Utiliser la condfiguration centrale du mouvement"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "Détecteur de mouvement entity id",
|
||||
"motion_sensor_entity_id": "Id d'entité du détecteur de mouvement",
|
||||
"motion_delay": "Délai avant activation lorsqu'un mouvement est détecté (secondss)",
|
||||
"motion_off_delai": "Délai avant désactivation lorsqu'aucun mouvement n'est détecté (secondes)",
|
||||
"motion_preset": "Preset à utiliser si mouvement détecté",
|
||||
"no_motion_preset": "Preset à utiliser si pas de mouvement détecté"
|
||||
"no_motion_preset": "Preset à utiliser si pas de mouvement détecté",
|
||||
"use_motion_central_config": "Cochez pour utiliser la configuration centrale du mouvement. Décochez et saisissez les attributs pour utiliser une configuration spécifique du mouvement"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Gestion de l'énergie",
|
||||
"description": "Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum.\nDonnez les entity id des capteurs qui mesurent la puissance totale et la puissance max autorisée.\nEnsuite donnez la puissance de l'équipement.\nTous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W).",
|
||||
"title": "Puissance - {name}",
|
||||
"description": "Gestion de la puissance. Sélectionne automatiquement le preset 'power' si la puissance consommée est supérieure à un maximum. Tous les capteurs et la puissance consommée par l'équipement doivent avoir la même unité de mesure (kW ou W).",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "Capteur de puissance totale (entity id)",
|
||||
"power_sensor_entity_id": "Puissance totale",
|
||||
"max_power_sensor_entity_id": "Capteur de puissance Max (entity id)",
|
||||
"power_temp": "Température si délestaqe"
|
||||
"power_temp": "Température si délestaqe",
|
||||
"use_power_central_config": "Utiliser la configuration centrale de la puissance"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "Entity id du capteur de puissance totale du logement",
|
||||
"max_power_sensor_entity_id": "Entity id du capteur de puissance Max autorisée avant délestage",
|
||||
"power_temp": "Température cible si délestaqe",
|
||||
"use_power_central_config": "Cochez pour utiliser la configuration centrale de la puissance. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la puissance"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"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.",
|
||||
"title": "Présence - {name}",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Capteur de présence",
|
||||
"eco_away_temp": "preset Eco",
|
||||
@@ -346,40 +414,45 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "preset Boost en mode AC",
|
||||
"use_presence_central_config": "Utiliser la configuration centrale de la présence"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Capteur de présence entity id (true si quelqu'un est présent)",
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence",
|
||||
"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"
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC",
|
||||
"use_presence_central_config": "Cochez pour utiliser la configuration centrale de la présence. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la présence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Parameters avancés",
|
||||
"description": "Configuration des paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.",
|
||||
"title": "Avancés - {name}",
|
||||
"description": "Paramètres avancés. Laissez les valeurs par défaut si vous ne savez pas ce que vous faites.\nCes paramètres peuvent induire des mauvais comportements du thermostat.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Délai minimal d'activation",
|
||||
"security_delay_min": "Délai maximal entre 2 mesures de températures",
|
||||
"security_min_on_percent": "Pourcentage minimal de puissance",
|
||||
"security_default_on_percent": "Pourcentage de puissance a utiliser en mode securité"
|
||||
"security_default_on_percent": "Pourcentage de puissance a utiliser en mode securité",
|
||||
"use_advanced_central_config": "Utiliser la configuration centrale avancée"
|
||||
},
|
||||
"data_description": {
|
||||
"minimal_activation_delay": "Délai en seondes en-dessous duquel l'équipement ne sera pas activé",
|
||||
"security_delay_min": "Délai maximal autorisé en minutes entre 2 mesures de températures. Au-dessus de ce délai, le thermostat se mettra en position de sécurité",
|
||||
"security_min_on_percent": "Seuil minimal de pourcentage de chauffage en-dessous duquel le préréglage sécurité ne sera jamais activé",
|
||||
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité"
|
||||
"security_default_on_percent": "Valeur par défaut pour le pourcentage de chauffage en mode sécurité. Mettre 0 pour éteindre le radiateur en mode sécurité",
|
||||
"use_advanced_central_config": "Cochez pour utiliser la configuration centrale avancée. Décochez et saisissez les attributs pour utiliser une configuration spécifique avancée"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Erreur inattendue",
|
||||
"unknown_entity": "entity id inconnu",
|
||||
"window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux."
|
||||
"window_open_detection_method": "Une seule méthode de détection des ouvertures ouvertes doit être utilisée. Utilisez le détecteur d'ouverture ou les seuils de température mais pas les deux.",
|
||||
"no_central_config": "Vous ne pouvez pas cocher 'Utiliser la configuration centrale' car aucune configuration centrale n'a été trouvée. Vous devez créer un Versatile Thermostat de type 'Central Configuration' pour pouvoir l'utiliser."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Le device est déjà configuré"
|
||||
@@ -388,6 +461,7 @@
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_central_config": "Configuration centrale",
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat",
|
||||
"thermostat_over_valve": "Thermostat sur une valve"
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
"flow_title": "Všestranná konfigurácia termostatu",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Typ všestranného termostatu",
|
||||
"data": {
|
||||
"thermostat_type": "Typ termostatu"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Len jeden centrálny typ konfigurácie je možný"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Pridajte nový všestranný termostat",
|
||||
"description": "Hlavné povinné atribúty",
|
||||
"data": {
|
||||
@@ -15,10 +24,17 @@
|
||||
"temp_min": "Minimálna povolená teplota",
|
||||
"temp_max": "Maximálna povolená teplota",
|
||||
"device_power": "Napájanie zariadenia",
|
||||
"use_central_mode": "Povoliť ovládanie centrálnou entitou (potrebná centrálna konfigurácia)",
|
||||
"use_window_feature": "Použite detekciu okien",
|
||||
"use_motion_feature": "Použite detekciu pohybu",
|
||||
"use_power_feature": "Použite správu napájania",
|
||||
"use_presence_feature": "Použite detekciu prítomnosti"
|
||||
"use_presence_feature": "Použite detekciu prítomnosti",
|
||||
"use_main_central_config": "Použite centrálnu hlavnú konfiguráciu"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Zaškrtnutím povolíte ovládanie VTherm pomocou vybraných entít central_mode",
|
||||
"use_main_central_config": "Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu. Zrušte začiarknutie, ak chcete použiť špecifickú hlavnú konfiguráciu pre tento VTherm",
|
||||
"external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -39,11 +55,11 @@
|
||||
"valve_entity2_id": "2. ventil číslo",
|
||||
"valve_entity3_id": "3. ventil číslo",
|
||||
"valve_entity4_id": "4. ventil číslo",
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period",
|
||||
"inverse_switch_command": "Inverse switch command",
|
||||
"auto_fan_mode": " Auto fan mode"
|
||||
"auto_regulation_mode": "Samoregulácia",
|
||||
"auto_regulation_dtemp": "Regulačný prah",
|
||||
"auto_regulation_periode_min": "Regulačné minimálne obdobie",
|
||||
"inverse_switch_command": "Inverzný prepínací príkaz",
|
||||
"auto_fan_mode": "Režim automatického ventilátora"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "ID entity povinného ohrievača",
|
||||
@@ -60,11 +76,11 @@
|
||||
"valve_entity2_id": "2. ventil číslo entity id",
|
||||
"valve_entity3_id": "3. ventil číslo entity id",
|
||||
"valve_entity4_id": "4. ventil číslo 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_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",
|
||||
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
|
||||
"auto_regulation_mode": "Automatické nastavenie cieľovej teploty",
|
||||
"auto_regulation_dtemp": "Hranica v °, pod ktorou sa zmena teploty neodošle",
|
||||
"auto_regulation_periode_min": "Trvanie v minútach medzi dvoma aktualizáciami predpisov",
|
||||
"inverse_switch_command": "V prípade spínača s pilotným vodičom a diódou možno budete musieť príkaz invertovať",
|
||||
"auto_fan_mode": "Automaticky aktivujte ventilátor, keď je potrebné veľké vykurovanie/chladenie"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -72,7 +88,13 @@
|
||||
"description": "Časovo proporcionálne integrálne atribúty",
|
||||
"data": {
|
||||
"tpi_coef_int": "Koeficient na použitie pre vnútornú teplotnú deltu",
|
||||
"tpi_coef_ext": "Koeficient na použitie pre deltu vonkajšej teploty"
|
||||
"tpi_coef_ext": "Koeficient na použitie pre deltu vonkajšej teploty",
|
||||
"use_tpi_central_config": "Použite centrálnu konfiguráciu TPI"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Koeficient na použitie pre vnútornú teplotnú deltu",
|
||||
"tpi_coef_ext": "Koeficient na použitie pre deltu vonkajšej teploty",
|
||||
"use_tpi_central_config": "Začiarknite, ak chcete použiť centrálnu konfiguráciu TPI. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu TPI pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
@@ -85,7 +107,18 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC",
|
||||
"use_presets_central_config": "Použite konfiguráciu centrálnych predvolieb"
|
||||
},
|
||||
"data_description": {
|
||||
"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 prednastavenej ochrane proti mrazu",
|
||||
"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",
|
||||
"use_presets_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnych predvolieb. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu predvolieb pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -96,14 +129,16 @@
|
||||
"window_delay": "Oneskorenie snímača okna (sekundy)",
|
||||
"window_auto_open_threshold": "Prah poklesu teploty pre automatickú detekciu otvoreného okna (v °/hodina)",
|
||||
"window_auto_close_threshold": "Prahová hodnota zvýšenia teploty pre koniec automatickej detekcie (v °/hodina)",
|
||||
"window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)"
|
||||
"window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)",
|
||||
"use_window_central_config": "Použite centrálnu konfiguráciu okna"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Nechajte prázdne, ak nemáte použiť žiadny okenný senzor",
|
||||
"window_delay": "Zohľadňuje sa oneskorenie v sekundách pred detekciou snímača",
|
||||
"window_auto_open_threshold": "Odporúčaná hodnota: medzi 3 a 10. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"window_auto_close_threshold": "Odporúčaná hodnota: 0. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne"
|
||||
"window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
@@ -114,14 +149,16 @@
|
||||
"motion_delay": "Oneskorenie aktivácie",
|
||||
"motion_off_delay": "Oneskorenie deaktivácie",
|
||||
"motion_preset": "Prednastavený pohyb",
|
||||
"no_motion_preset": "Žiadna predvoľba pohybu"
|
||||
"no_motion_preset": "Žiadna predvoľba pohybu",
|
||||
"use_motion_central_config": "Použite centrálnu konfiguráciu pohybu"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "ID entity snímača pohybu",
|
||||
"motion_delay": "Oneskorenie aktivácie pohybu (sekundy)",
|
||||
"motion_off_delay": "Oneskorenie deaktivácie pohybu (sekundy)",
|
||||
"motion_preset": "Prednastavené na použitie pri detekcii pohybu",
|
||||
"no_motion_preset": "Prednastavené na použitie, keď nie je detekovaný žiadny pohyb"
|
||||
"no_motion_preset": "Prednastavené na použitie, keď nie je detekovaný žiadny pohyb",
|
||||
"use_motion_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho pohybu. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu pohybu pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
@@ -130,7 +167,14 @@
|
||||
"data": {
|
||||
"power_sensor_entity_id": "ID entity snímača výkonu",
|
||||
"max_power_sensor_entity_id": "ID entity snímača maximálneho výkonu",
|
||||
"power_temp": "Teplota pre zníženie výkonu"
|
||||
"power_temp": "Teplota pre zníženie výkonu",
|
||||
"use_power_central_config": "Použite centrálnu konfiguráciu napájania"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "ID entity snímača výkonu",
|
||||
"max_power_sensor_entity_id": "ID entity snímača maximálneho výkonu",
|
||||
"power_temp": "Teplota pre zníženie výkonu",
|
||||
"use_power_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho napájania. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu napájania pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
@@ -144,7 +188,19 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC",
|
||||
"use_presence_central_config": "Použite centrálnu konfiguráciu prítomnosti"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "ID entity senzora prítomnosti",
|
||||
"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": "Teplota v Prednastavená ochrana pred mrazom, 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",
|
||||
"use_presence_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnej prítomnosti. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu prítomnosti pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -154,20 +210,23 @@
|
||||
"minimal_activation_delay": "Minimálne oneskorenie aktivácie",
|
||||
"security_delay_min": "Bezpečnostné oneskorenie (v minútach)",
|
||||
"security_min_on_percent": "Minimálne percento výkonu na aktiváciu bezpečnostného režimu",
|
||||
"security_default_on_percent": "Percento výkonu na použitie v bezpečnostnom režime"
|
||||
"security_default_on_percent": "Percento výkonu na použitie v bezpečnostnom režime",
|
||||
"use_advanced_central_config": "Použite centrálnu rozšírenú konfiguráciu"
|
||||
},
|
||||
"data_description": {
|
||||
"minimal_activation_delay": "Oneskorenie v sekundách, pri ktorom sa zariadenie neaktivuje",
|
||||
"security_delay_min": "Maximálne povolené oneskorenie v minútach medzi dvoma meraniami teploty. Po uplynutí tohto oneskorenia sa termostat prepne do bezpečnostného vypnutého stavu",
|
||||
"security_min_on_percent": "Minimálna percentuálna hodnota ohrevu pre aktiváciu prednastavenej bezpečnosti. Pod týmto percentom výkonu termostat neprejde do prednastavenia zabezpečenia",
|
||||
"security_default_on_percent": "Predvolená percentuálna hodnota vykurovacieho výkonu v bezpečnostnej predvoľbe. Nastavte na 0, ak chcete vypnúť ohrievač v zabezpečenom stave"
|
||||
"security_default_on_percent": "Predvolená percentuálna hodnota vykurovacieho výkonu v bezpečnostnej predvoľbe. Nastavte na 0, ak chcete vypnúť ohrievač v zabezpečenom stave",
|
||||
"use_advanced_central_config": "Začiarknite, ak chcete použiť centrálnu rozšírenú konfiguráciu. Zrušte začiarknutie, ak chcete použiť špecifickú rozšírenú konfiguráciu pre tento VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Neočakávaná chyba",
|
||||
"unknown_entity": "Neznáme ID entity",
|
||||
"window_open_detection_method": "Mala by sa použiť iba jedna metóda detekcie otvoreného okna. Použite senzor alebo automatickú detekciu cez teplotný prah, ale nie oboje"
|
||||
"window_open_detection_method": "Mala by sa použiť iba jedna metóda detekcie otvoreného okna. Použite senzor alebo automatickú detekciu cez teplotný prah, ale nie oboje",
|
||||
"no_central_config": "Nemôžete zaškrtnúť „použiť centrálnu konfiguráciu“, pretože sa nenašla žiadna centrálna konfigurácia. Aby ste ho mohli používať, musíte si vytvoriť všestranný termostat typu „Central Configuration“."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Zariadenie je už nakonfigurované"
|
||||
@@ -177,7 +236,16 @@
|
||||
"flow_title": "Všestranná konfigurácia termostatu",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Pridajte nový všestranný termostat",
|
||||
"title": "Typ - {name}",
|
||||
"data": {
|
||||
"thermostat_type": "Typ termostatu"
|
||||
},
|
||||
"data_description": {
|
||||
"thermostat_type": "Je možný len jeden centrálny typ konfigurácie"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Hlavný - {name}",
|
||||
"description": "Hlavné povinné atribúty",
|
||||
"data": {
|
||||
"name": "Názov",
|
||||
@@ -188,14 +256,21 @@
|
||||
"temp_min": "Minimálna povolená teplota",
|
||||
"temp_max": "Maximálna povolená teplota",
|
||||
"device_power": "Výkon zariadenia (kW)",
|
||||
"use_central_mode": "Povoliť ovládanie centrálnou entitou (potrebná centrálna konfigurácia)",
|
||||
"use_window_feature": "Použite detekciu okien",
|
||||
"use_motion_feature": "Použite detekciu pohybu",
|
||||
"use_power_feature": "Použite správu napájania",
|
||||
"use_presence_feature": "Použite detekciu prítomnosti"
|
||||
"use_presence_feature": "Použite detekciu prítomnosti",
|
||||
"use_main_central_config": "Použite centrálnu hlavnú konfiguráciu"
|
||||
},
|
||||
"data_description": {
|
||||
"use_central_mode": "Zaškrtnutím povolíte ovládanie VTherm pomocou vybraných entít central_mode",
|
||||
"use_main_central_config": "Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu. Ak chcete použiť špecifickú konfiguráciu pre tento VTherm, zrušte začiarknutie",
|
||||
"external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Prepojené entity",
|
||||
"title": "Prepojené entity - {name}",
|
||||
"description": "Atribúty prepojených entít",
|
||||
"data": {
|
||||
"heater_entity_id": "Spínač ohrievača",
|
||||
@@ -212,11 +287,11 @@
|
||||
"valve_entity2_id": "2. ventil číslo",
|
||||
"valve_entity3_id": "3. ventil číslo",
|
||||
"valve_entity4_id": "4. ventil číslo",
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period",
|
||||
"inverse_switch_command": "Inverse switch command",
|
||||
"auto_fan_mode": " Auto fan mode"
|
||||
"auto_regulation_mode": "Samoregulácia",
|
||||
"auto_regulation_dtemp": "Regulačný prah",
|
||||
"auto_regulation_periode_min": "Regulačné minimálne obdobie",
|
||||
"inverse_switch_command": "Inverzný prepínací príkaz",
|
||||
"auto_fan_mode": "Režim automatického ventilátora"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "ID entity povinného ohrievača",
|
||||
@@ -233,23 +308,29 @@
|
||||
"valve_entity2_id": "2. ventil číslo entity id",
|
||||
"valve_entity3_id": "3. ventil číslo entity id",
|
||||
"valve_entity4_id": "4. ventil číslo 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_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",
|
||||
"auto_fan_mode": " Automatically activate fan when huge heating/cooling is necessary"
|
||||
"auto_regulation_mode": "Automatické nastavenie cieľovej teploty",
|
||||
"auto_regulation_dtemp": "Hranica v °, pod ktorou sa zmena teploty neodošle",
|
||||
"auto_regulation_periode_min": "Trvanie v minútach medzi dvoma aktualizáciami predpisov",
|
||||
"inverse_switch_command": "V prípade spínača s pilotným vodičom a diódou možno budete musieť príkaz invertovať",
|
||||
"auto_fan_mode": "Automaticky aktivujte ventilátor, keď je potrebné veľké vykurovanie/chladenie"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
"title": "TPI",
|
||||
"title": "TPI - {name}",
|
||||
"description": "Časovo proporcionálne integrálne atribúty",
|
||||
"data": {
|
||||
"tpi_coef_int": "Koeficient na použitie pre vnútornú teplotnú deltu",
|
||||
"tpi_coef_ext": "Koeficient na použitie pre vonkajšiu teplotnú deltu"
|
||||
"tpi_coef_ext": "Koeficient na použitie pre vonkajšiu teplotnú deltu",
|
||||
"use_tpi_central_config": "Použite centrálnu konfiguráciu TPI"
|
||||
},
|
||||
"data_description": {
|
||||
"tpi_coef_int": "Koeficient na použitie pre vnútornú teplotnú deltu",
|
||||
"tpi_coef_ext": "Koeficient na použitie pre deltu vonkajšej teploty",
|
||||
"use_tpi_central_config": "Začiarknite, ak chcete použiť centrálnu konfiguráciu TPI. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu TPI pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"title": "Predvoľby",
|
||||
"title": "Predvoľby - {name}",
|
||||
"description": "Pre každú predvoľbu zadajte cieľovú teplotu (0, ak chcete predvoľbu ignorovať)",
|
||||
"data": {
|
||||
"eco_temp": "Teplota v predvoľbe Eco",
|
||||
@@ -258,52 +339,74 @@
|
||||
"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"
|
||||
"boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC",
|
||||
"use_presets_central_config": "Použite konfiguráciu centrálnych predvolieb"
|
||||
},
|
||||
"data_description": {
|
||||
"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 prednastavenej ochrane proti mrazu",
|
||||
"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",
|
||||
"use_presets_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnych predvolieb. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu predvolieb pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"title": "Správa okien",
|
||||
"title": "Správa okien - {name}",
|
||||
"description": "Otvoriť správu okien.\nAk sa príslušné entity_id nepoužíva, ponechajte prázdne\nMôžete tiež nakonfigurovať automatickú detekciu otvoreného okna na základe poklesu teploty",
|
||||
"data": {
|
||||
"window_sensor_entity_id": "ID entity snímača okna",
|
||||
"window_delay": "Oneskorenie snímača okna (sekundy)",
|
||||
"window_auto_open_threshold": "Prah poklesu teploty pre automatickú detekciu otvoreného okna (v °/hodina)",
|
||||
"window_auto_close_threshold": "Prahová hodnota zvýšenia teploty pre koniec automatickej detekcie (v °/hodina)",
|
||||
"window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)"
|
||||
"window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)",
|
||||
"use_window_central_config": "Použite centrálnu konfiguráciu okna"
|
||||
},
|
||||
"data_description": {
|
||||
"window_sensor_entity_id": "Nechajte prázdne, ak nemáte použiť žiadny okenný senzor",
|
||||
"window_delay": "Zohľadňuje sa oneskorenie v sekundách pred detekciou snímača",
|
||||
"window_auto_open_threshold": "Odporúčaná hodnota: medzi 3 a 10. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"window_auto_close_threshold": "Odporúčaná hodnota: 0. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne"
|
||||
"window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne",
|
||||
"use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"motion": {
|
||||
"title": "Riadenie pohybu",
|
||||
"title": "Riadenie pohybu - {name}",
|
||||
"description": "Správa snímača pohybu. Predvoľba sa môže automaticky prepínať v závislosti od detekcie pohybu\nAk sa nepoužíva, ponechajte zodpovedajúce entity_id prázdne.\nmotion_preset a no_motion_preset by mali byť nastavené na zodpovedajúci názov predvoľby",
|
||||
"data": {
|
||||
"motion_sensor_entity_id": "ID entity snímača pohybu",
|
||||
"motion_delay": "Oneskorenie aktivácie",
|
||||
"motion_off_delay": "Oneskorenie deaktivácie",
|
||||
"motion_preset": "Prednastavený pohyb",
|
||||
"no_motion_preset": "Žiadna predvoľba pohybu"
|
||||
"no_motion_preset": "Žiadna predvoľba pohybu",
|
||||
"use_motion_central_config": "Použite centrálnu konfiguráciu pohybu"
|
||||
},
|
||||
"data_description": {
|
||||
"motion_sensor_entity_id": "ID entity snímača pohybu",
|
||||
"motion_delay": "Oneskorenie aktivácie pohybu (sekundy)",
|
||||
"motion_off_delay": "Oneskorenie deaktivácie pohybu (sekundy)",
|
||||
"motion_preset": "Prednastavené na použitie pri detekcii pohybu",
|
||||
"no_motion_preset": "Prednastavené na použitie, keď nie je detekovaný žiadny pohyb"
|
||||
"no_motion_preset": "Prednastavené na použitie, keď nie je detekovaný žiadny pohyb",
|
||||
"use_motion_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho pohybu. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu pohybu pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"power": {
|
||||
"title": "Správa napájania",
|
||||
"title": "Správa napájania - {name}",
|
||||
"description": "Atribúty správy napájania.\nPoskytuje senzor výkonu a maximálneho výkonu vášho domova.\nPotom zadajte spotrebu energie ohrievača, keď je zapnutý.\nVšetky senzory a výkon zariadenia by mali mať rovnakú jednotku (kW alebo W).\nPonechajte zodpovedajúce entity_id prázdne ak sa nepoužíva.",
|
||||
"data": {
|
||||
"power_sensor_entity_id": "ID entity snímača výkonu",
|
||||
"max_power_sensor_entity_id": "ID entity snímača maximálneho výkonu",
|
||||
"power_temp": "Teplota pre zníženie výkonu"
|
||||
"power_temp": "Teplota pre zníženie výkonu",
|
||||
"use_power_central_config": "Použite centrálnu konfiguráciu napájania"
|
||||
},
|
||||
"data_description": {
|
||||
"power_sensor_entity_id": "ID entity snímača výkonu",
|
||||
"max_power_sensor_entity_id": "ID entity snímača maximálneho výkonu",
|
||||
"power_temp": "Teplota pre zníženie výkonu",
|
||||
"use_power_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho napájania. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu napájania pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
@@ -317,30 +420,45 @@
|
||||
"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"
|
||||
"boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC",
|
||||
"use_presence_central_config": "Použite centrálnu konfiguráciu prítomnosti"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "ID entity senzora prítomnosti",
|
||||
"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": "Teplota v Prednastavená ochrana pred mrazom, 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",
|
||||
"use_presence_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnej prítomnosti. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu prítomnosti pre tento VTherm"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Pokročilé parametre",
|
||||
"title": "Pokročilé parametre - {name}",
|
||||
"description": "Konfigurácia pokročilých parametrov. Ak neviete, čo robíte, ponechajte predvolené hodnoty.\nTento parameter môže viesť k veľmi zlej regulácii teploty alebo výkonu.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Minimálne oneskorenie aktivácie",
|
||||
"security_delay_min": "Bezpečnostné oneskorenie (v minútach)",
|
||||
"security_min_on_percent": "Minimálne percento výkonu pre bezpečnostný režim",
|
||||
"security_default_on_percent": "Percento výkonu na použitie v bezpečnostnom režime"
|
||||
"security_default_on_percent": "Percento výkonu na použitie v bezpečnostnom režime",
|
||||
"use_advanced_central_config": "Použite centrálnu rozšírenú konfiguráciu"
|
||||
},
|
||||
"data_description": {
|
||||
"minimal_activation_delay": "Oneskorenie v sekundách, pri ktorom sa zariadenie neaktivuje",
|
||||
"security_delay_min": "Maximálne povolené oneskorenie v minútach medzi dvoma meraniami teploty. Po uplynutí tohto oneskorenia sa termostat prepne do bezpečnostného vypnutého stavu",
|
||||
"security_min_on_percent": "Minimálna percentuálna hodnota ohrevu pre aktiváciu prednastavenej bezpečnosti. Pod týmto percentom výkonu termostat neprejde do prednastavenia zabezpečenia",
|
||||
"security_default_on_percent": "Predvolená percentuálna hodnota vykurovacieho výkonu v bezpečnostnej predvoľbe. Nastavte na 0, ak chcete vypnúť ohrievač v zabezpečenom stave"
|
||||
"security_default_on_percent": "Predvolená percentuálna hodnota vykurovacieho výkonu v bezpečnostnej predvoľbe. Nastavte na 0, ak chcete vypnúť ohrievač v zabezpečenom stave",
|
||||
"use_advanced_central_config": "Začiarknite, ak chcete použiť centrálnu rozšírenú konfiguráciu. Zrušte začiarknutie, ak chcete použiť špecifickú rozšírenú konfiguráciu pre tento VTherm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "Neočakávaná chyba",
|
||||
"unknown_entity": "Neznáme ID entity",
|
||||
"window_open_detection_method": "Mala by sa použiť iba jedna metóda detekcie otvoreného okna. Použite senzor alebo automatickú detekciu cez teplotný prah, ale nie oboje"
|
||||
"window_open_detection_method": "Mala by sa použiť iba jedna metóda detekcie otvoreného okna. Použite senzor alebo automatickú detekciu cez teplotný prah, ale nie oboje",
|
||||
"no_central_config": "Nemôžete zaškrtnúť „použiť centrálnu konfiguráciu“, pretože sa nenašla žiadna centrálna konfigurácia. Aby ste ho mohli používať, musíte si vytvoriť všestranný termostat typu „Central Configuration“."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Zariadenie je už nakonfigurované"
|
||||
@@ -349,27 +467,28 @@
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_central_config": "Centrálna konfigurácia",
|
||||
"thermostat_over_switch": "Termostat nad spínačom",
|
||||
"thermostat_over_climate": "Termostat nad iným termostatom",
|
||||
"thermostat_over_valve": "Thermostat over a valve"
|
||||
"thermostat_over_valve": "Termostat nad ventilom"
|
||||
}
|
||||
},
|
||||
"auto_regulation_mode": {
|
||||
"options": {
|
||||
"auto_regulation_slow": "Slow",
|
||||
"auto_regulation_strong": "Strong",
|
||||
"auto_regulation_medium": "Medium",
|
||||
"auto_regulation_light": "Light",
|
||||
"auto_regulation_expert": "Expert",
|
||||
"auto_regulation_none": "No auto-regulation"
|
||||
"auto_regulation_slow": "Pomalé",
|
||||
"auto_regulation_strong": "Silné",
|
||||
"auto_regulation_medium": "Stredné",
|
||||
"auto_regulation_light": "Jemné",
|
||||
"auto_regulation_expert": "Expertné",
|
||||
"auto_regulation_none": "Nie auto-regulácia"
|
||||
}
|
||||
},
|
||||
"auto_fan_mode": {
|
||||
"options": {
|
||||
"auto_fan_none": "No auto-fan",
|
||||
"auto_fan_low": "Low",
|
||||
"auto_fan_medium": "Medium",
|
||||
"auto_fan_high": "High",
|
||||
"auto_fan_none": "Žiadny automatický ventilátor",
|
||||
"auto_fan_low": "Nízky",
|
||||
"auto_fan_medium": "Stredný",
|
||||
"auto_fan_high": "Vysoký",
|
||||
"auto_fan_turbo": "Turbo"
|
||||
}
|
||||
}
|
||||
@@ -389,4 +508,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Any
|
||||
from enum import StrEnum
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, UnitOfTemperature
|
||||
from homeassistant.core import State
|
||||
|
||||
from homeassistant.exceptions import ServiceNotFound
|
||||
|
||||
@@ -111,18 +112,18 @@ class UnderlyingEntity:
|
||||
# This should be the correct way to handle turn_off and turn_on but this breaks the unit test
|
||||
# will an not understandable error: TypeError: object MagicMock can't be used in 'await' expression
|
||||
async def turn_off(self):
|
||||
""" Turn off the underlying equipement.
|
||||
Need to be overriden"""
|
||||
"""Turn off the underlying equipement.
|
||||
Need to be overriden"""
|
||||
return NotImplementedError
|
||||
|
||||
async def turn_on(self):
|
||||
""" Turn off the underlying equipement.
|
||||
Need to be overriden"""
|
||||
"""Turn off the underlying equipement.
|
||||
Need to be overriden"""
|
||||
return NotImplementedError
|
||||
|
||||
@property
|
||||
def is_inversed(self):
|
||||
""" Tells if the switch command should be inversed"""
|
||||
"""Tells if the switch command should be inversed"""
|
||||
return False
|
||||
|
||||
def remove_entity(self):
|
||||
@@ -140,8 +141,9 @@ class UnderlyingEntity:
|
||||
await self.set_hvac_mode(hvac_mode)
|
||||
elif hvac_mode != HVACMode.OFF and not self.is_device_active:
|
||||
_LOGGER.warning(
|
||||
"%s - The hvac mode is ON, but the underlying device is not ON. Turning on device %s",
|
||||
"%s - The hvac mode is %s, but the underlying device is not ON. Turning on device %s if needed",
|
||||
self,
|
||||
hvac_mode,
|
||||
self._entity_id,
|
||||
)
|
||||
await self.set_hvac_mode(hvac_mode)
|
||||
@@ -164,7 +166,11 @@ class UnderlyingEntity:
|
||||
"""Starting cycle for switch"""
|
||||
|
||||
def _cancel_cycle(self):
|
||||
""" Stops an eventual cycle """
|
||||
"""Stops an eventual cycle"""
|
||||
|
||||
def cap_sent_value(self, value) -> float:
|
||||
"""capping of the value send to the underlying eqt"""
|
||||
return value
|
||||
|
||||
|
||||
class UnderlyingSwitch(UnderlyingEntity):
|
||||
@@ -205,7 +211,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
@overrides
|
||||
@property
|
||||
def is_inversed(self):
|
||||
""" Tells if the switch command should be inversed"""
|
||||
"""Tells if the switch command should be inversed"""
|
||||
return self._thermostat.is_inversed
|
||||
|
||||
# @overrides this breaks some unit tests TypeError: object MagicMock can't be used in 'await' expression
|
||||
@@ -227,14 +233,16 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
def is_device_active(self):
|
||||
"""If the toggleable device is currently active."""
|
||||
real_state = self._hass.states.is_state(self._entity_id, STATE_ON)
|
||||
return (self.is_inversed and not real_state) or (not self.is_inversed and real_state)
|
||||
return (self.is_inversed and not real_state) or (
|
||||
not self.is_inversed and real_state
|
||||
)
|
||||
|
||||
# @overrides this breaks some unit tests TypeError: object MagicMock can't be used in 'await' expression
|
||||
async def turn_off(self):
|
||||
"""Turn heater toggleable device off."""
|
||||
_LOGGER.debug("%s - Stopping underlying entity %s", self, self._entity_id)
|
||||
command = SERVICE_TURN_OFF if not self.is_inversed else SERVICE_TURN_ON
|
||||
domain = self._entity_id.split('.')[0]
|
||||
domain = self._entity_id.split(".")[0]
|
||||
# This may fails if called after shutdown
|
||||
try:
|
||||
data = {ATTR_ENTITY_ID: self._entity_id}
|
||||
@@ -250,7 +258,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
"""Turn heater toggleable device on."""
|
||||
_LOGGER.debug("%s - Starting underlying entity %s", self, self._entity_id)
|
||||
command = SERVICE_TURN_ON if not self.is_inversed else SERVICE_TURN_OFF
|
||||
domain = self._entity_id.split('.')[0]
|
||||
domain = self._entity_id.split(".")[0]
|
||||
try:
|
||||
data = {ATTR_ENTITY_ID: self._entity_id}
|
||||
await self._hass.services.async_call(
|
||||
@@ -261,7 +269,6 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
except ServiceNotFound as err:
|
||||
_LOGGER.error(err)
|
||||
|
||||
|
||||
@overrides
|
||||
async def start_cycle(
|
||||
self,
|
||||
@@ -348,7 +355,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
if await self._thermostat.check_overpowering():
|
||||
_LOGGER.debug("%s - End of cycle (3)", self)
|
||||
return
|
||||
# Security mode could have change the on_time percent
|
||||
# safety mode could have change the on_time percent
|
||||
await self._thermostat.check_security()
|
||||
time = self._on_time_sec
|
||||
|
||||
@@ -477,6 +484,14 @@ class UnderlyingClimate(UnderlyingEntity):
|
||||
if not self.is_initialized:
|
||||
return False
|
||||
|
||||
if self._underlying_climate.hvac_mode == hvac_mode:
|
||||
_LOGGER.debug(
|
||||
"%s - hvac_mode is already is requested state %s. Do not send any command",
|
||||
self,
|
||||
self._underlying_climate.hvac_mode,
|
||||
)
|
||||
return False
|
||||
|
||||
data = {ATTR_ENTITY_ID: self._entity_id, "hvac_mode": hvac_mode}
|
||||
await self._hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
@@ -490,10 +505,14 @@ class UnderlyingClimate(UnderlyingEntity):
|
||||
def is_device_active(self):
|
||||
"""If the toggleable device is currently active."""
|
||||
if self.is_initialized:
|
||||
return self._underlying_climate.hvac_mode != HVACMode.OFF and self._underlying_climate.hvac_action not in [
|
||||
HVACAction.IDLE,
|
||||
HVACAction.OFF,
|
||||
]
|
||||
return (
|
||||
self._underlying_climate.hvac_mode != HVACMode.OFF
|
||||
and self._underlying_climate.hvac_action
|
||||
not in [
|
||||
HVACAction.IDLE,
|
||||
HVACAction.OFF,
|
||||
]
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -550,7 +569,7 @@ class UnderlyingClimate(UnderlyingEntity):
|
||||
return
|
||||
data = {
|
||||
ATTR_ENTITY_ID: self._entity_id,
|
||||
"temperature": temperature,
|
||||
"temperature": self.cap_sent_value(temperature),
|
||||
"target_temp_high": max_temp,
|
||||
"target_temp_low": min_temp,
|
||||
}
|
||||
@@ -664,6 +683,40 @@ class UnderlyingClimate(UnderlyingEntity):
|
||||
return None
|
||||
return self._underlying_climate.turn_aux_heat_off()
|
||||
|
||||
@overrides
|
||||
def cap_sent_value(self, value) -> float:
|
||||
"""Try to adapt the target temp value to the min_temp / max_temp found
|
||||
in the underlying entity (if any)"""
|
||||
|
||||
if not self.is_initialized:
|
||||
return value
|
||||
|
||||
# Gets the min_temp and max_temp
|
||||
if (
|
||||
self._underlying_climate.min_temp is not None
|
||||
and self._underlying_climate is not None
|
||||
):
|
||||
min_val = self._underlying_climate.min_temp
|
||||
max_val = self._underlying_climate.max_temp
|
||||
|
||||
new_value = max(min_val, min(value, max_val))
|
||||
else:
|
||||
_LOGGER.debug("%s - no min and max attributes on underlying", self)
|
||||
new_value = value
|
||||
|
||||
if new_value != value:
|
||||
_LOGGER.info(
|
||||
"%s - Target temp have been updated due min, max of the underlying entity. new_value=%.0f value=%.0f min=%.0f max=%.0f",
|
||||
self,
|
||||
new_value,
|
||||
value,
|
||||
min_val,
|
||||
max_val,
|
||||
)
|
||||
|
||||
return new_value
|
||||
|
||||
|
||||
class UnderlyingValve(UnderlyingEntity):
|
||||
"""Represent a underlying switch"""
|
||||
|
||||
@@ -672,10 +725,7 @@ class UnderlyingValve(UnderlyingEntity):
|
||||
_percent_open: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
thermostat: Any,
|
||||
valve_entity_id: str
|
||||
self, hass: HomeAssistant, thermostat: Any, valve_entity_id: str
|
||||
) -> None:
|
||||
"""Initialize the underlying switch"""
|
||||
|
||||
@@ -689,13 +739,14 @@ class UnderlyingValve(UnderlyingEntity):
|
||||
self._should_relaunch_control_heating = False
|
||||
self._hvac_mode = None
|
||||
self._percent_open = self._thermostat.valve_open_percent
|
||||
self._valve_entity_id = valve_entity_id
|
||||
|
||||
async def send_percent_open(self):
|
||||
""" Send the percent open to the underlying valve """
|
||||
"""Send the percent open to the underlying valve"""
|
||||
# This may fails if called after shutdown
|
||||
try:
|
||||
data = { ATTR_ENTITY_ID: self._entity_id, "value": self._percent_open }
|
||||
domain = self._entity_id.split('.')[0]
|
||||
data = {ATTR_ENTITY_ID: self._entity_id, "value": self._percent_open}
|
||||
domain = self._entity_id.split(".")[0]
|
||||
await self._hass.services.async_call(
|
||||
domain,
|
||||
SERVICE_SET_VALUE,
|
||||
@@ -734,7 +785,7 @@ class UnderlyingValve(UnderlyingEntity):
|
||||
# To test if real device is open but this is causing some side effect
|
||||
# because the activation can be deferred -
|
||||
# or float(self._hass.states.get(self._entity_id).state) > 0
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
return False
|
||||
|
||||
@overrides
|
||||
@@ -748,11 +799,43 @@ class UnderlyingValve(UnderlyingEntity):
|
||||
):
|
||||
"""We use this function to change the on_percent"""
|
||||
if force:
|
||||
self._percent_open = self.cap_sent_value(self._percent_open)
|
||||
await self.send_percent_open()
|
||||
|
||||
def set_valve_open_percent(self, percent):
|
||||
""" Update the valve open percent """
|
||||
caped_val = self._thermostat.valve_open_percent
|
||||
@overrides
|
||||
def cap_sent_value(self, value) -> float:
|
||||
"""Try to adapt the open_percent value to the min / max found
|
||||
in the underlying entity (if any)"""
|
||||
|
||||
# Gets the last number state
|
||||
valve_state: State = self._hass.states.get(self._valve_entity_id)
|
||||
if valve_state is None:
|
||||
return value
|
||||
|
||||
if "min" in valve_state.attributes and "max" in valve_state.attributes:
|
||||
min_val = valve_state.attributes["min"]
|
||||
max_val = valve_state.attributes["max"]
|
||||
|
||||
new_value = round(max(min_val, min(value, max_val)))
|
||||
else:
|
||||
_LOGGER.debug("%s - no min and max attributes on underlying", self)
|
||||
new_value = value
|
||||
|
||||
if new_value != value:
|
||||
_LOGGER.info(
|
||||
"%s - Valve open percent have been updated due min, max of the underlying entity. new_value=%.0f value=%.0f min=%.0f max=%.0f",
|
||||
self,
|
||||
new_value,
|
||||
value,
|
||||
min_val,
|
||||
max_val,
|
||||
)
|
||||
|
||||
return new_value
|
||||
|
||||
def set_valve_open_percent(self):
|
||||
"""Update the valve open percent"""
|
||||
caped_val = self.cap_sent_value(self._thermostat.valve_open_percent)
|
||||
if self._percent_open == caped_val:
|
||||
# No changes
|
||||
return
|
||||
@@ -760,7 +843,9 @@ class UnderlyingValve(UnderlyingEntity):
|
||||
self._percent_open = caped_val
|
||||
# Send the new command to valve via a service call
|
||||
|
||||
_LOGGER.info("%s - Setting valve ouverture percent to %s", self, self._percent_open)
|
||||
_LOGGER.info(
|
||||
"%s - Setting valve ouverture percent to %s", self, self._percent_open
|
||||
)
|
||||
# Send the change to the valve, in background
|
||||
self._hass.create_task(self.send_percent_open())
|
||||
|
||||
|
||||
@@ -3,7 +3,13 @@ import logging
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
from .const import DOMAIN, CONF_AUTO_REGULATION_EXPERT, CONF_SHORT_EMA_PARAMS
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
CONF_AUTO_REGULATION_EXPERT,
|
||||
CONF_SHORT_EMA_PARAMS,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
VTHERM_API_NAME = "vtherm_api"
|
||||
|
||||
@@ -13,41 +19,61 @@ _LOGGER = logging.getLogger(__name__)
|
||||
class VersatileThermostatAPI(dict):
|
||||
"""The VersatileThermostatAPI"""
|
||||
|
||||
_hass: HomeAssistant
|
||||
# _entries: Dict(str, ConfigEntry)
|
||||
_hass: HomeAssistant = None
|
||||
|
||||
@classmethod
|
||||
def get_vtherm_api(cls, hass: HomeAssistant):
|
||||
"""Get the eventual VTherm API class instance"""
|
||||
ret = hass.data.get(DOMAIN).get(VTHERM_API_NAME)
|
||||
def get_vtherm_api(cls, hass=None):
|
||||
"""Get the eventual VTherm API class instance or
|
||||
instantiate it if it doesn't exists"""
|
||||
if hass is not None:
|
||||
VersatileThermostatAPI._hass = hass
|
||||
|
||||
if VersatileThermostatAPI._hass is None:
|
||||
return None
|
||||
|
||||
domain = VersatileThermostatAPI._hass.data.get(DOMAIN)
|
||||
if not domain:
|
||||
VersatileThermostatAPI._hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
ret = VersatileThermostatAPI._hass.data.get(DOMAIN).get(VTHERM_API_NAME)
|
||||
if ret is None:
|
||||
ret = VersatileThermostatAPI(hass)
|
||||
hass.data[DOMAIN][VTHERM_API_NAME] = ret
|
||||
ret = VersatileThermostatAPI()
|
||||
VersatileThermostatAPI._hass.data[DOMAIN][VTHERM_API_NAME] = ret
|
||||
return ret
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
def __init__(self) -> None:
|
||||
_LOGGER.debug("building a VersatileThermostatAPI")
|
||||
super().__init__()
|
||||
self._hass = hass
|
||||
self._expert_params = None
|
||||
self._short_ema_params = None
|
||||
|
||||
def find_central_configuration(self):
|
||||
"""Search for a central configuration"""
|
||||
for config_entry in VersatileThermostatAPI._hass.config_entries.async_entries(
|
||||
DOMAIN
|
||||
):
|
||||
if (
|
||||
config_entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
== CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
):
|
||||
central_config = config_entry
|
||||
return central_config
|
||||
return None
|
||||
|
||||
def add_entry(self, entry: ConfigEntry):
|
||||
"""Add a new entry"""
|
||||
_LOGGER.debug("Add the entry %s", entry.entry_id)
|
||||
# self._entries[entry.entry_id] = entry
|
||||
# Add the entry in hass.data
|
||||
self._hass.data[DOMAIN][entry.entry_id] = entry
|
||||
VersatileThermostatAPI._hass.data[DOMAIN][entry.entry_id] = entry
|
||||
|
||||
def remove_entry(self, entry: ConfigEntry):
|
||||
"""Remove an entry"""
|
||||
_LOGGER.debug("Remove the entry %s", entry.entry_id)
|
||||
# self._entries.pop(entry.entry_id)
|
||||
self._hass.data[DOMAIN].pop(entry.entry_id)
|
||||
VersatileThermostatAPI._hass.data[DOMAIN].pop(entry.entry_id)
|
||||
# If not more entries are preset, remove the API
|
||||
if len(self) == 0:
|
||||
_LOGGER.debug("No more entries-> Remove the API from DOMAIN")
|
||||
self._hass.data.pop(DOMAIN)
|
||||
VersatileThermostatAPI._hass.data.pop(DOMAIN)
|
||||
|
||||
def set_global_config(self, config):
|
||||
"""Read the global configuration from configuration.yaml file"""
|
||||
@@ -74,4 +100,4 @@ class VersatileThermostatAPI(dict):
|
||||
@property
|
||||
def hass(self):
|
||||
"""Get the HomeAssistant object"""
|
||||
return self._hass
|
||||
return VersatileThermostatAPI._hass
|
||||
|
||||
BIN
images/central_mode.png
Normal file
BIN
images/central_mode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 45 KiB |
BIN
images/config-main0.png
Normal file
BIN
images/config-main0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
images/plotly-curves.png
Normal file
BIN
images/plotly-curves.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
7
pyrightconfig.json
Normal file
7
pyrightconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"include": [
|
||||
"custom_components/versatile_thermostat/**",
|
||||
"homeassistant/**"
|
||||
],
|
||||
"reportShadowedImports": false
|
||||
}
|
||||
112
tests/commons.py
112
tests/commons.py
@@ -29,13 +29,19 @@ from custom_components.versatile_thermostat.commons import ( # pylint: disable=
|
||||
NowClass,
|
||||
)
|
||||
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
||||
MOCK_TH_OVER_4SWITCH_USER_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_USER_CONFIG,
|
||||
MOCK_TH_OVER_SWITCH_MAIN_CONFIG,
|
||||
MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG,
|
||||
MOCK_TH_OVER_SWITCH_TYPE_CONFIG,
|
||||
MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG,
|
||||
MOCK_TH_OVER_4SWITCH_TYPE_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_MAIN_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG,
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG,
|
||||
@@ -56,8 +62,11 @@ from .const import ( # pylint: disable=unused-import
|
||||
PRESET_ACTIVITY,
|
||||
)
|
||||
|
||||
|
||||
FULL_SWITCH_CONFIG = (
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
@@ -70,6 +79,8 @@ FULL_SWITCH_CONFIG = (
|
||||
|
||||
FULL_SWITCH_AC_CONFIG = (
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
| MOCK_PRESETS_AC_CONFIG
|
||||
@@ -83,6 +94,8 @@ FULL_SWITCH_AC_CONFIG = (
|
||||
|
||||
PARTIAL_CLIMATE_CONFIG = (
|
||||
MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
@@ -90,6 +103,8 @@ PARTIAL_CLIMATE_CONFIG = (
|
||||
|
||||
PARTIAL_CLIMATE_NOT_REGULATED_CONFIG = (
|
||||
MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
@@ -98,6 +113,8 @@ PARTIAL_CLIMATE_NOT_REGULATED_CONFIG = (
|
||||
PARTIAL_CLIMATE_AC_CONFIG = (
|
||||
MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
)
|
||||
@@ -114,6 +131,45 @@ FULL_4SWITCH_CONFIG = (
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
)
|
||||
|
||||
FULL_CENTRAL_CONFIG = {
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17.1,
|
||||
"comfort_temp": 0,
|
||||
"boost_temp": 19.1,
|
||||
"eco_ac_temp": 25.1,
|
||||
"comfort_ac_temp": 23.1,
|
||||
"boost_ac_temp": 21.1,
|
||||
"frost_away_temp": 15.1,
|
||||
"eco_away_temp": 15.2,
|
||||
"comfort_away_temp": 0,
|
||||
"boost_away_temp": 15.4,
|
||||
"eco_ac_away_temp": 30.5,
|
||||
"comfort_ac_away_temp": 0,
|
||||
"boost_ac_away_temp": 30.7,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -129,6 +185,7 @@ class MockClimate(ClimateEntity):
|
||||
hvac_mode: HVACMode = HVACMode.OFF,
|
||||
hvac_action: HVACAction = HVACAction.OFF,
|
||||
fan_modes: list[str] = None,
|
||||
hvac_modes: list[str] = None,
|
||||
) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
@@ -144,7 +201,11 @@ class MockClimate(ClimateEntity):
|
||||
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_hvac_modes = (
|
||||
hvac_modes
|
||||
if hvac_modes is not None
|
||||
else [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
|
||||
)
|
||||
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
self._attr_target_temperature = 20
|
||||
self._attr_current_temperature = 15
|
||||
@@ -280,6 +341,14 @@ class MagicMockClimate(MagicMock):
|
||||
def supported_features(self): # pylint: disable=missing-function-docstring
|
||||
return ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
|
||||
@property
|
||||
def min_temp(self): # pylint: disable=missing-function-docstring
|
||||
return 15
|
||||
|
||||
@property
|
||||
def max_temp(self): # pylint: disable=missing-function-docstring
|
||||
return 19
|
||||
|
||||
|
||||
async def create_thermostat(
|
||||
hass: HomeAssistant, entry: MockConfigEntry, entity_id: str
|
||||
@@ -302,6 +371,27 @@ async def create_thermostat(
|
||||
return search_entity(hass, entity_id, CLIMATE_DOMAIN)
|
||||
|
||||
|
||||
async def create_central_config( # pylint: disable=dangerous-default-value
|
||||
hass: HomeAssistant, entry: MockConfigEntry = FULL_CENTRAL_CONFIG
|
||||
):
|
||||
"""Creates a Central Configuration from entry given in argument"""
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data=entry,
|
||||
)
|
||||
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# Test that VTherm API find the CentralConfig
|
||||
api = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
central_configuration = api.find_central_configuration()
|
||||
assert central_configuration is not None
|
||||
|
||||
|
||||
def search_entity(hass: HomeAssistant, entity_id, domain) -> Entity:
|
||||
"""Search and return the entity in the domain"""
|
||||
component = hass.data[domain]
|
||||
@@ -311,6 +401,12 @@ def search_entity(hass: HomeAssistant, entity_id, domain) -> Entity:
|
||||
return None
|
||||
|
||||
|
||||
def count_entities(hass: HomeAssistant, entity_id, domain) -> Entity:
|
||||
"""Search and return the entity in the domain"""
|
||||
component = hass.data[domain]
|
||||
return len(list(component.entities)) if component.entities else 0
|
||||
|
||||
|
||||
async def send_temperature_change_event(
|
||||
entity: BaseThermostat, new_temp, date, sleep=True
|
||||
):
|
||||
@@ -332,10 +428,12 @@ async def send_temperature_change_event(
|
||||
)
|
||||
},
|
||||
)
|
||||
await entity._async_temperature_changed(temp_event)
|
||||
dearm_window_auto = await entity._async_temperature_changed(temp_event)
|
||||
if sleep:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
return dearm_window_auto
|
||||
|
||||
|
||||
async def send_ext_temperature_change_event(
|
||||
entity: BaseThermostat, new_temp, date, sleep=True
|
||||
@@ -523,6 +621,7 @@ async def send_climate_change_event(
|
||||
old_hvac_action: HVACAction,
|
||||
date,
|
||||
sleep=True,
|
||||
underlying_entity_id: str = None,
|
||||
):
|
||||
"""Sending a new climate event simulating a change on the underlying climate state"""
|
||||
_LOGGER.info(
|
||||
@@ -534,18 +633,23 @@ async def send_climate_change_event(
|
||||
date,
|
||||
entity,
|
||||
)
|
||||
|
||||
send_from_entity_id = (
|
||||
underlying_entity_id if underlying_entity_id is not None else entity.entity_id
|
||||
)
|
||||
|
||||
climate_event = Event(
|
||||
EVENT_STATE_CHANGED,
|
||||
{
|
||||
"new_state": State(
|
||||
entity_id=entity.entity_id,
|
||||
entity_id=send_from_entity_id,
|
||||
state=new_hvac_mode,
|
||||
attributes={"hvac_action": new_hvac_action},
|
||||
last_changed=date,
|
||||
last_updated=date,
|
||||
),
|
||||
"old_state": State(
|
||||
entity_id=entity.entity_id,
|
||||
entity_id=send_from_entity_id,
|
||||
state=old_hvac_mode,
|
||||
attributes={"hvac_action": old_hvac_action},
|
||||
last_changed=date,
|
||||
|
||||
@@ -26,15 +26,20 @@ from custom_components.versatile_thermostat.config_flow import (
|
||||
VersatileThermostatBaseConfigFlow,
|
||||
)
|
||||
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
|
||||
from .commons import create_central_config
|
||||
|
||||
pytest_plugins = "pytest_homeassistant_custom_component" # pylint: disable=invalid-name
|
||||
|
||||
|
||||
# This fixture enables loading custom integrations in all tests.
|
||||
# Remove to enable selective use of this fixture
|
||||
@pytest.fixture(autouse=True)
|
||||
def auto_enable_custom_integrations(enable_custom_integrations): # pylint: disable=unused-argument
|
||||
def auto_enable_custom_integrations(
|
||||
enable_custom_integrations,
|
||||
): # pylint: disable=unused-argument
|
||||
"""Enable all integration in tests"""
|
||||
yield
|
||||
|
||||
@@ -108,3 +113,20 @@ def skip_send_event_fixture():
|
||||
"""Skip the send_event in BaseThermostat"""
|
||||
with patch.object(BaseThermostat, "send_event"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="init_vtherm_api")
|
||||
def init_vtherm_api_fixture(hass):
|
||||
"""Initialize the VTherm API"""
|
||||
VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(name="init_central_config")
|
||||
async def init_central_config_fixture(
|
||||
hass, init_vtherm_api
|
||||
): # pylint: disable=unused-argument
|
||||
"""Initialize the VTherm API"""
|
||||
await create_central_config(hass)
|
||||
|
||||
yield
|
||||
|
||||
104
tests/const.py
104
tests/const.py
@@ -6,74 +6,23 @@ from homeassistant.components.climate.const import ( # pylint: disable=unused-i
|
||||
PRESET_NONE,
|
||||
PRESET_ACTIVITY,
|
||||
)
|
||||
from custom_components.versatile_thermostat.const import (
|
||||
CONF_NAME,
|
||||
CONF_HEATER,
|
||||
CONF_HEATER_2,
|
||||
CONF_HEATER_3,
|
||||
CONF_HEATER_4,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_AC_MODE,
|
||||
CONF_TEMP_SENSOR,
|
||||
CONF_EXTERNAL_TEMP_SENSOR,
|
||||
CONF_CYCLE_MIN,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
CONF_PROP_FUNCTION,
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_TPI_COEF_INT,
|
||||
CONF_TPI_COEF_EXT,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
CONF_SECURITY_MIN_ON_PERCENT,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_WINDOW_SENSOR,
|
||||
CONF_WINDOW_DELAY,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION,
|
||||
CONF_MOTION_SENSOR,
|
||||
CONF_MOTION_DELAY,
|
||||
CONF_MOTION_OFF_DELAY,
|
||||
CONF_MOTION_PRESET,
|
||||
CONF_NO_MOTION_PRESET,
|
||||
CONF_POWER_SENSOR,
|
||||
CONF_MAX_POWER_SENSOR,
|
||||
CONF_DEVICE_POWER,
|
||||
CONF_PRESET_POWER,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
PRESET_AWAY_SUFFIX,
|
||||
CONF_CLIMATE,
|
||||
CONF_AUTO_REGULATION_MODE,
|
||||
CONF_AUTO_REGULATION_STRONG,
|
||||
CONF_AUTO_REGULATION_NONE,
|
||||
CONF_AUTO_REGULATION_DTEMP,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||
CONF_INVERSE_SWITCH,
|
||||
CONF_AUTO_FAN_HIGH,
|
||||
CONF_AUTO_FAN_MODE,
|
||||
PRESET_FROST_PROTECTION,
|
||||
)
|
||||
|
||||
from custom_components.versatile_thermostat.const import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_SWITCH_MAIN_CONFIG = {
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
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,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: True,
|
||||
CONF_USE_POWER_FEATURE: True,
|
||||
CONF_USE_PRESENCE_FEATURE: True,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_4SWITCH_USER_CONFIG = {
|
||||
@@ -92,14 +41,31 @@ MOCK_TH_OVER_4SWITCH_USER_CONFIG = {
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_CLIMATE_USER_CONFIG = {
|
||||
CONF_NAME: "TheOverClimateMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
|
||||
}
|
||||
|
||||
|
||||
MOCK_TH_OVER_CLIMATE_MAIN_CONFIG = {
|
||||
CONF_NAME: "TheOverClimateMockName",
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: False,
|
||||
CONF_USE_CENTRAL_MODE: True
|
||||
# Keep default values which are False
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG = {
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
# Keep default values which are False
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_SWITCH_CENTRAL_MAIN_CONFIG = {
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_DEVICE_POWER: 1
|
||||
# Keep default values which are False
|
||||
}
|
||||
|
||||
@@ -174,6 +140,11 @@ MOCK_PRESETS_AC_CONFIG = {
|
||||
|
||||
MOCK_WINDOW_CONFIG = {
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
|
||||
# Not used normally only for tests to avoid rewrite all tests
|
||||
CONF_WINDOW_DELAY: 10,
|
||||
}
|
||||
|
||||
MOCK_WINDOW_DELAY_CONFIG = {
|
||||
CONF_WINDOW_DELAY: 10,
|
||||
}
|
||||
|
||||
@@ -228,3 +199,14 @@ MOCK_DEFAULT_FEATURE_CONFIG = {
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
}
|
||||
|
||||
MOCK_DEFAULT_CENTRAL_CONFIG = {
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: False,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: False,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: False,
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ async def test_over_climate_regulation(
|
||||
assert entity.name == "TheOverClimateMockName"
|
||||
assert entity.is_over_climate is True
|
||||
assert entity.is_regulated is True
|
||||
assert entity.hvac_action is HVACAction.OFF
|
||||
assert entity.hvac_mode is HVACMode.OFF
|
||||
assert entity.hvac_action is HVACAction.OFF
|
||||
assert entity.target_temperature == entity.min_temp
|
||||
assert entity.preset_modes == [
|
||||
PRESET_NONE,
|
||||
|
||||
@@ -6,6 +6,10 @@ from datetime import datetime, timedelta
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
)
|
||||
|
||||
from .commons import *
|
||||
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
@@ -354,7 +358,7 @@ async def test_bug_82(
|
||||
skip_turn_on_off_heater,
|
||||
skip_send_event,
|
||||
):
|
||||
"""Test that when a underlying climate is not available the VTherm doesn't go into security mode"""
|
||||
"""Test that when a underlying climate is not available the VTherm doesn't go into safety mode"""
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
@@ -423,7 +427,7 @@ async def test_bug_82(
|
||||
assert mock_find_climate.mock_calls[0] == call()
|
||||
mock_find_climate.assert_has_calls([call.find_underlying_entity()])
|
||||
|
||||
# Force security mode
|
||||
# Force safety mode
|
||||
assert entity._last_ext_temperature_measure is not None
|
||||
assert entity._last_temperature_measure is not None
|
||||
assert (
|
||||
@@ -568,3 +572,143 @@ async def test_bug_101(
|
||||
)
|
||||
assert entity.target_temperature == 12.75
|
||||
assert entity.preset_mode is PRESET_NONE
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_bug_272(
|
||||
hass: HomeAssistant,
|
||||
skip_hass_states_is_state,
|
||||
skip_turn_on_off_heater,
|
||||
skip_send_event,
|
||||
):
|
||||
"""Test that it not possible to set the target temperature under the min_temp setting"""
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverClimateMockName",
|
||||
unique_id="uniqueId",
|
||||
data=PARTIAL_CLIMATE_CONFIG, # 5 minutes security delay
|
||||
)
|
||||
|
||||
# Min_temp is 15 and max_temp is 19
|
||||
fake_underlying_climate = MagicMockClimate()
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||
return_value=fake_underlying_climate,
|
||||
), patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call:
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
def find_my_entity(entity_id) -> ClimateEntity:
|
||||
"""Find my new entity"""
|
||||
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
|
||||
for entity in component.entities:
|
||||
if entity.entity_id == entity_id:
|
||||
return entity
|
||||
|
||||
entity = find_my_entity("climate.theoverclimatemockname")
|
||||
|
||||
assert entity
|
||||
|
||||
assert entity.name == "TheOverClimateMockName"
|
||||
assert entity.is_over_climate is True
|
||||
assert entity.hvac_mode is HVACMode.OFF
|
||||
assert entity.target_temperature == entity.min_temp
|
||||
assert entity.is_regulated is True
|
||||
|
||||
assert mock_service_call.call_count == 0
|
||||
|
||||
# Set the hvac_mode to HEAT
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
|
||||
# In the accepted interval
|
||||
await entity.async_set_temperature(temperature=17.5)
|
||||
|
||||
# MagicMock climate is already HEAT by default. So there is no SET_HAVC_MODE call
|
||||
assert mock_service_call.call_count == 1
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
# call.async_call(
|
||||
# "climate",
|
||||
# SERVICE_SET_HVAC_MODE,
|
||||
# {"entity_id": "climate.mock_climate", "hvac_mode": HVACMode.HEAT},
|
||||
# ),
|
||||
call.async_call(
|
||||
"climate",
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
"entity_id": "climate.mock_climate",
|
||||
"temperature": 17.5,
|
||||
"target_temp_high": 30,
|
||||
"target_temp_low": 15,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
), patch("homeassistant.core.ServiceRegistry.async_call") as mock_service_call:
|
||||
# Set room temperature to something very cold
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
|
||||
await send_temperature_change_event(entity, 13, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 9, event_timestamp)
|
||||
|
||||
# Not in the accepted interval (15-19)
|
||||
await entity.async_set_temperature(temperature=10)
|
||||
assert mock_service_call.call_count == 1
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"climate",
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
"entity_id": "climate.mock_climate",
|
||||
"temperature": 15, # the minimum acceptable
|
||||
"target_temp_high": 30,
|
||||
"target_temp_low": 15,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
), patch("homeassistant.core.ServiceRegistry.async_call") as mock_service_call:
|
||||
# Set room temperature to something very cold
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
|
||||
await send_temperature_change_event(entity, 13, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 9, event_timestamp)
|
||||
|
||||
# In the accepted interval
|
||||
await entity.async_set_temperature(temperature=20.8)
|
||||
assert mock_service_call.call_count == 1
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"climate",
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
"entity_id": "climate.mock_climate",
|
||||
"temperature": 19, # the maximum acceptable
|
||||
"target_temp_high": 30,
|
||||
"target_temp_low": 15,
|
||||
},
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
442
tests/test_central_config.py
Normal file
442
tests/test_central_config.py
Normal file
@@ -0,0 +1,442 @@
|
||||
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
|
||||
|
||||
""" Test the central_configuration """
|
||||
from unittest.mock import patch # , call
|
||||
|
||||
# from datetime import datetime # , timedelta
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
# from homeassistant.components.climate import HVACAction, HVACMode
|
||||
from homeassistant.config_entries import ConfigEntryState, SOURCE_USER
|
||||
|
||||
# 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 custom_components.versatile_thermostat.thermostat_switch import (
|
||||
ThermostatOverSwitch,
|
||||
)
|
||||
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
|
||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
from .const 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_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
"""Tests the clean_central_config_doubon of base_thermostat"""
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_central_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17.1,
|
||||
"comfort_temp": 18.1,
|
||||
"boost_temp": 19.1,
|
||||
"eco_ac_temp": 25.1,
|
||||
"comfort_ac_temp": 23.1,
|
||||
"boost_ac_temp": 21.1,
|
||||
"frost_away_temp": 15.1,
|
||||
"eco_away_temp": 15.2,
|
||||
"comfort_away_temp": 15.3,
|
||||
"boost_away_temp": 15.4,
|
||||
"eco_ac_away_temp": 30.5,
|
||||
"comfort_ac_away_temp": 30.6,
|
||||
"boost_ac_away_temp": 30.7,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_central_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
},
|
||||
)
|
||||
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity: ThermostatOverClimate = search_entity(
|
||||
hass, "climate.thecentralconfigmockname", "climate"
|
||||
)
|
||||
|
||||
assert entity is None
|
||||
|
||||
assert count_entities(hass, "climate.thecentralconfigmockname", "climate") == 0
|
||||
|
||||
# Test that VTherm API find the CentralConfig
|
||||
api = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
central_configuration = api.find_central_configuration()
|
||||
assert central_configuration is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_minimal_over_switch_wo_central_config(
|
||||
hass: HomeAssistant, skip_hass_states_is_state, init_vtherm_api
|
||||
):
|
||||
"""Tests that a VTherm without any central_configuration is working with its own attributes"""
|
||||
# Add a Switch VTherm
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverSwitchMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_TEMP_MIN: 8,
|
||||
CONF_TEMP_MAX: 18,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17,
|
||||
"comfort_temp": 18,
|
||||
"boost_temp": 21,
|
||||
CONF_USE_WINDOW_FEATURE: False,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_HEATER: "switch.mock_switch",
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.01,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
|
||||
# CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1,
|
||||
# CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
||||
# CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test
|
||||
CONF_INVERSE_SWITCH: True,
|
||||
# CONF_USE_MAIN_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_TPI_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_PRESETS_CENTRAL_CONFIG: False,
|
||||
# CONF_USE_ADVANCED_CENTRAL_CONFIG: False,
|
||||
},
|
||||
)
|
||||
|
||||
with patch("homeassistant.core.ServiceRegistry.async_call"):
|
||||
entity: ThermostatOverSwitch = await create_thermostat(
|
||||
hass, entry, "climate.theoverswitchmockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.name == "TheOverSwitchMockName"
|
||||
assert entity.is_over_switch
|
||||
assert entity._temp_sensor_entity_id == "sensor.mock_temp_sensor"
|
||||
assert entity._ext_temp_sensor_entity_id == "sensor.mock_ext_temp_sensor"
|
||||
assert entity._cycle_min == 5
|
||||
assert entity._attr_min_temp == 8
|
||||
assert entity._attr_max_temp == 18
|
||||
assert entity.preset_modes == ["none", "frost", "eco", "comfort", "boost"]
|
||||
assert entity.is_window_auto_enabled is False
|
||||
assert entity.nb_underlying_entities == 1
|
||||
assert entity.underlying_entity_id(0) == "switch.mock_switch"
|
||||
assert entity.proportional_algorithm is not None
|
||||
assert entity.proportional_algorithm._tpi_coef_int == 0.3
|
||||
assert entity.proportional_algorithm._tpi_coef_ext == 0.01
|
||||
assert entity.proportional_algorithm._minimal_activation_delay == 30
|
||||
assert entity._security_delay_min == 5
|
||||
assert entity._security_min_on_percent == 0.3
|
||||
assert entity._security_default_on_percent == 0.1
|
||||
assert entity.is_inversed
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_full_over_switch_wo_central_config(
|
||||
hass: HomeAssistant, skip_hass_states_is_state, init_vtherm_api
|
||||
):
|
||||
"""Tests that a VTherm without any central_configuration is working with its own attributes"""
|
||||
# Add a Switch VTherm
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverSwitchMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_TEMP_MIN: 8,
|
||||
CONF_TEMP_MAX: 18,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17,
|
||||
"comfort_temp": 18,
|
||||
"boost_temp": 21,
|
||||
"frost_away_temp": 13,
|
||||
"eco_away_temp": 13,
|
||||
"comfort_away_temp": 13,
|
||||
"boost_away_temp": 13,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: True,
|
||||
CONF_USE_POWER_FEATURE: True,
|
||||
CONF_USE_PRESENCE_FEATURE: True,
|
||||
CONF_HEATER: "switch.mock_switch",
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.01,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor",
|
||||
CONF_WINDOW_DELAY: 30,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 3,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 5,
|
||||
CONF_MOTION_DELAY: 10,
|
||||
CONF_MOTION_OFF_DELAY: 29,
|
||||
CONF_MOTION_PRESET: "comfort",
|
||||
CONF_NO_MOTION_PRESET: "eco",
|
||||
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
|
||||
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
|
||||
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: False,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: False,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: False,
|
||||
},
|
||||
)
|
||||
|
||||
with patch("homeassistant.core.ServiceRegistry.async_call"):
|
||||
entity: ThermostatOverSwitch = await create_thermostat(
|
||||
hass, entry, "climate.theoverswitchmockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.name == "TheOverSwitchMockName"
|
||||
assert entity.is_over_switch
|
||||
assert entity._temp_sensor_entity_id == "sensor.mock_temp_sensor"
|
||||
assert entity._ext_temp_sensor_entity_id == "sensor.mock_ext_temp_sensor"
|
||||
assert entity._cycle_min == 5
|
||||
assert entity._attr_min_temp == 8
|
||||
assert entity._attr_max_temp == 18
|
||||
assert entity.preset_modes == [
|
||||
"none",
|
||||
"frost",
|
||||
"eco",
|
||||
"comfort",
|
||||
"boost",
|
||||
"activity",
|
||||
]
|
||||
assert entity.nb_underlying_entities == 1
|
||||
assert entity.underlying_entity_id(0) == "switch.mock_switch"
|
||||
assert entity.proportional_algorithm is not None
|
||||
assert entity.proportional_algorithm._tpi_coef_int == 0.3
|
||||
assert entity.proportional_algorithm._tpi_coef_ext == 0.01
|
||||
assert entity.proportional_algorithm._minimal_activation_delay == 30
|
||||
assert entity._security_delay_min == 5
|
||||
assert entity._security_min_on_percent == 0.3
|
||||
assert entity._security_default_on_percent == 0.1
|
||||
assert entity.is_inversed is False
|
||||
|
||||
assert entity.is_window_auto_enabled is False # we have an entity_id
|
||||
assert entity._window_sensor_entity_id == "binary_sensor.mock_window_sensor"
|
||||
assert entity._window_delay_sec == 30
|
||||
assert entity._window_auto_close_threshold == 0.1
|
||||
assert entity._window_auto_open_threshold == 3
|
||||
assert entity._window_auto_max_duration == 5
|
||||
|
||||
assert entity._motion_sensor_entity_id == "binary_sensor.mock_motion_sensor"
|
||||
assert entity._motion_delay_sec == 10
|
||||
assert entity._motion_off_delay_sec == 29
|
||||
assert entity._motion_preset == "comfort"
|
||||
assert entity._no_motion_preset == "eco"
|
||||
|
||||
assert entity._power_sensor_entity_id == "sensor.mock_power_sensor"
|
||||
assert entity._max_power_sensor_entity_id == "sensor.mock_max_power_sensor"
|
||||
|
||||
assert entity._presence_sensor_entity_id == "binary_sensor.mock_presence_sensor"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_full_over_switch_with_central_config(
|
||||
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
|
||||
):
|
||||
"""Tests that a VTherm with central_configuration is working with the central_config attributes"""
|
||||
# Add a Switch VTherm
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverSwitchMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_TEMP_MIN: 8,
|
||||
CONF_TEMP_MAX: 18,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17,
|
||||
"comfort_temp": 18,
|
||||
"boost_temp": 21,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: True,
|
||||
CONF_USE_POWER_FEATURE: True,
|
||||
CONF_USE_PRESENCE_FEATURE: True,
|
||||
CONF_HEATER: "switch.mock_switch",
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.01,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor",
|
||||
CONF_WINDOW_DELAY: 30,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 3,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 5,
|
||||
CONF_MOTION_DELAY: 10,
|
||||
CONF_MOTION_OFF_DELAY: 29,
|
||||
CONF_MOTION_PRESET: "comfort",
|
||||
CONF_NO_MOTION_PRESET: "eco",
|
||||
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
|
||||
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
|
||||
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
with patch("homeassistant.core.ServiceRegistry.async_call"):
|
||||
entity: ThermostatOverSwitch = await create_thermostat(
|
||||
hass, entry, "climate.theoverswitchmockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.name == "TheOverSwitchMockName"
|
||||
assert entity.is_over_switch
|
||||
assert entity._temp_sensor_entity_id == "sensor.mock_temp_sensor"
|
||||
assert entity._ext_temp_sensor_entity_id == "sensor.mock_ext_temp_sensor"
|
||||
assert entity._cycle_min == 5
|
||||
assert entity._attr_min_temp == 15
|
||||
assert entity._attr_max_temp == 30
|
||||
assert entity.preset_modes == [
|
||||
"none",
|
||||
"frost",
|
||||
"eco",
|
||||
"boost",
|
||||
"activity",
|
||||
]
|
||||
assert entity.nb_underlying_entities == 1
|
||||
assert entity.underlying_entity_id(0) == "switch.mock_switch"
|
||||
assert entity.proportional_algorithm is not None
|
||||
assert entity.proportional_algorithm._tpi_coef_int == 0.5
|
||||
assert entity.proportional_algorithm._tpi_coef_ext == 0.02
|
||||
assert entity.proportional_algorithm._minimal_activation_delay == 11
|
||||
assert entity._security_delay_min == 61
|
||||
assert entity._security_min_on_percent == 0.5
|
||||
assert entity._security_default_on_percent == 0.2
|
||||
assert entity.is_inversed is False
|
||||
|
||||
# We have an entity so window auto is not enabled
|
||||
assert entity.is_window_auto_enabled is False
|
||||
assert entity._window_sensor_entity_id == "binary_sensor.mock_window_sensor"
|
||||
assert entity._window_delay_sec == 15
|
||||
assert entity._window_auto_close_threshold == 1
|
||||
assert entity._window_auto_open_threshold == 4
|
||||
assert entity._window_auto_max_duration == 31
|
||||
|
||||
assert entity._motion_sensor_entity_id == "binary_sensor.mock_motion_sensor"
|
||||
assert entity._motion_delay_sec == 31
|
||||
assert entity._motion_off_delay_sec == 301
|
||||
assert entity._motion_preset == "boost"
|
||||
assert entity._no_motion_preset == "frost"
|
||||
|
||||
assert entity._power_sensor_entity_id == "sensor.mock_power_sensor"
|
||||
assert entity._max_power_sensor_entity_id == "sensor.mock_max_power_sensor"
|
||||
|
||||
assert entity._presence_sensor_entity_id == "binary_sensor.mock_presence_sensor"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_over_switch_with_central_config_but_no_central_config(
|
||||
hass: HomeAssistant, skip_hass_states_get, init_vtherm_api
|
||||
):
|
||||
"""Tests that a VTherm with a central_configuration flag but no central config. Should lead to an error"""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == SOURCE_USER
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
# in case of error we stays in main
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {"use_main_central_config": "no_central_config"}
|
||||
1040
tests/test_central_mode.py
Normal file
1040
tests/test_central_mode.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,13 +13,8 @@ from .const 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_show_form(hass: HomeAssistant) -> None:
|
||||
async def test_show_form(hass: HomeAssistant, init_vtherm_api) -> None:
|
||||
"""Test that the form is served with no input"""
|
||||
# Init the API
|
||||
# hass.data["custom_components"] = None
|
||||
# loader.async_get_custom_components(hass)
|
||||
# BaseThermostatAPI(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -33,9 +28,9 @@ async def test_show_form(hass: HomeAssistant) -> None:
|
||||
# Disable this test which don't work anymore (kill the pytest !)
|
||||
@pytest.mark.skip
|
||||
async def test_user_config_flow_over_switch(
|
||||
hass: HomeAssistant, skip_hass_states_get
|
||||
hass: HomeAssistant, skip_hass_states_get, init_central_config
|
||||
): # pylint: disable=unused-argument
|
||||
"""Test the config flow with all thermostat_over_switch features"""
|
||||
"""Test the config flow with all thermostat_over_switch features and central config on"""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -47,6 +42,14 @@ async def test_user_config_flow_over_switch(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_MAIN_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "type"
|
||||
assert result["errors"] == {}
|
||||
@@ -60,7 +63,7 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_TPI_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -68,7 +71,7 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_PRESETS_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -76,7 +79,11 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_WINDOW_CONFIG
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -84,7 +91,11 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_MOTION_CONFIG
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_MOTION_SENSOR: "input_boolean.motion_sensor",
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -92,7 +103,7 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_POWER_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_POWER_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -100,7 +111,11 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESENCE_CONFIG
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_PRESENCE_SENSOR: "person.presence_sensor",
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -108,21 +123,27 @@ async def test_user_config_flow_over_switch(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_ADVANCED_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_ADVANCED_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert (
|
||||
result["data"]
|
||||
== MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
assert result["data"] == (
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_MAIN_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_WINDOW_CONFIG
|
||||
| MOCK_MOTION_CONFIG
|
||||
| MOCK_POWER_CONFIG
|
||||
| MOCK_PRESENCE_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
| {CONF_WINDOW_SENSOR: "binary_sensor.window_sensor"}
|
||||
| {CONF_MOTION_SENSOR: "input_boolean.motion_sensor"}
|
||||
| {CONF_PRESENCE_SENSOR: "person.presence_sensor"}
|
||||
| {
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
}
|
||||
)
|
||||
assert result["result"]
|
||||
assert result["result"].domain == DOMAIN
|
||||
@@ -137,6 +158,8 @@ async def test_user_config_flow_over_climate(
|
||||
hass: HomeAssistant, skip_hass_states_get
|
||||
): # pylint: disable=unused-argument
|
||||
"""Test the config flow with all thermostat_over_climate features and no additional features"""
|
||||
await create_central_config(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -148,6 +171,22 @@ async def test_user_config_flow_over_climate(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_CLIMATE_MAIN_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "type"
|
||||
assert result["errors"] == {}
|
||||
@@ -160,6 +199,14 @@ async def test_user_config_flow_over_climate(
|
||||
assert result["step_id"] == "presets"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_PRESETS_CENTRAL_CONFIG: False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "presets"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||
)
|
||||
@@ -168,19 +215,31 @@ async def test_user_config_flow_over_climate(
|
||||
assert result["step_id"] == "advanced"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_ADVANCED_CENTRAL_CONFIG: False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "advanced"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_ADVANCED_CONFIG
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert (
|
||||
result["data"]
|
||||
== MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
| MOCK_DEFAULT_FEATURE_CONFIG
|
||||
)
|
||||
assert result[
|
||||
"data"
|
||||
] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | MOCK_PRESETS_CONFIG | MOCK_ADVANCED_CONFIG | MOCK_DEFAULT_FEATURE_CONFIG | {
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: False,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: False,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: False,
|
||||
}
|
||||
assert result["result"]
|
||||
assert result["result"].domain == DOMAIN
|
||||
assert result["result"].version == 1
|
||||
@@ -196,6 +255,8 @@ async def test_user_config_flow_window_auto_ok(
|
||||
skip_control_heating, # pylint: disable=unused-argument
|
||||
):
|
||||
"""Test the config flow with only window auto feature"""
|
||||
await create_central_config(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -206,18 +267,26 @@ async def test_user_config_flow_window_auto_ok(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
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,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -233,6 +302,14 @@ async def test_user_config_flow_window_auto_ok(
|
||||
assert result["step_id"] == "tpi"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_TPI_CENTRAL_CONFIG: False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "tpi"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
)
|
||||
@@ -242,7 +319,16 @@ async def test_user_config_flow_window_auto_ok(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_PRESETS_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "window"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_USE_WINDOW_CENTRAL_CONFIG: False},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -259,27 +345,35 @@ async def test_user_config_flow_window_auto_ok(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_ADVANCED_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_ADVANCED_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert (
|
||||
result["data"]
|
||||
== MOCK_TH_OVER_SWITCH_USER_CONFIG
|
||||
| {
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_WINDOW_DELAY: 30, # the default value is added
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
}
|
||||
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
|
||||
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
| MOCK_PRESETS_CONFIG
|
||||
| MOCK_WINDOW_AUTO_CONFIG
|
||||
| MOCK_ADVANCED_CONFIG
|
||||
)
|
||||
assert result["data"] == {
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_WINDOW_DELAY: 30, # the default value is added
|
||||
CONF_USE_CENTRAL_MODE: True, # True is the defaulf value
|
||||
} | MOCK_TH_OVER_SWITCH_TYPE_CONFIG | MOCK_TH_OVER_SWITCH_TPI_CONFIG | MOCK_WINDOW_AUTO_CONFIG | {
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
}
|
||||
assert result["result"]
|
||||
assert result["result"].domain == DOMAIN
|
||||
assert result["result"].version == 1
|
||||
@@ -293,6 +387,9 @@ async def test_user_config_flow_window_auto_ko(
|
||||
hass: HomeAssistant, skip_hass_states_get # pylint: disable=unused-argument
|
||||
):
|
||||
"""Test the config flow with window auto and window features -> not allowed"""
|
||||
|
||||
await create_central_config(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@@ -303,18 +400,26 @@ async def test_user_config_flow_window_auto_ko(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
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,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -330,6 +435,14 @@ async def test_user_config_flow_window_auto_ko(
|
||||
assert result["step_id"] == "tpi"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_USE_TPI_CENTRAL_CONFIG: False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "tpi"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
)
|
||||
@@ -339,7 +452,7 @@ async def test_user_config_flow_window_auto_ko(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_PRESETS_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -348,15 +461,29 @@ async def test_user_config_flow_window_auto_ko(
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=MOCK_WINDOW_AUTO_CONFIG | MOCK_WINDOW_CONFIG,
|
||||
user_input={
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
# We should stay on window with an error
|
||||
assert result["step_id"] == "window"
|
||||
assert result["errors"] == {
|
||||
"window_sensor_entity_id": "window_open_detection_method"
|
||||
}
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=MOCK_WINDOW_DELAY_CONFIG,
|
||||
)
|
||||
|
||||
# Since issue #280 we cannot have the error because we only display the
|
||||
# MOCK_WINDOW_DELAY_CONFIG form if we have a sensor configured
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
# We should stay on window with an error
|
||||
assert result["errors"] == {}
|
||||
# "window_sensor_entity_id": "window_open_detection_method"
|
||||
# }
|
||||
assert result["step_id"] == "advanced"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@@ -368,19 +495,23 @@ async def test_user_config_flow_over_4_switches(
|
||||
):
|
||||
"""Test the config flow with 4 switchs thermostat_over_switch features"""
|
||||
|
||||
SOURCE_CONFIG = { # pylint: disable=wildcard-import, invalid-name
|
||||
CONF_NAME: "TheOver4SwitchMockName",
|
||||
await create_central_config(hass)
|
||||
|
||||
SOURCE_CONFIG = {
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
}
|
||||
|
||||
MAIN_CONFIG = { # pylint: disable=wildcard-import, invalid-name
|
||||
CONF_NAME: "TheOver4SwitchMockName",
|
||||
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,
|
||||
CONF_DEVICE_POWER: 1,
|
||||
CONF_USE_WINDOW_FEATURE: False,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_CENTRAL_MODE: False,
|
||||
}
|
||||
|
||||
TYPE_CONFIG = { # pylint: disable=wildcard-import, invalid-name
|
||||
@@ -390,6 +521,7 @@ async def test_user_config_flow_over_4_switches(
|
||||
CONF_HEATER_4: "switch.mock_switch4",
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_AC_MODE: False,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
}
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -404,6 +536,15 @@ async def test_user_config_flow_over_4_switches(
|
||||
user_input=SOURCE_CONFIG,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "main"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=MAIN_CONFIG,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "type"
|
||||
assert result["errors"] == {}
|
||||
@@ -418,7 +559,7 @@ async def test_user_config_flow_over_4_switches(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_TPI_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -426,7 +567,7 @@ async def test_user_config_flow_over_4_switches(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_PRESETS_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
@@ -434,15 +575,25 @@ async def test_user_config_flow_over_4_switches(
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=MOCK_ADVANCED_CONFIG
|
||||
result["flow_id"], user_input={CONF_USE_ADVANCED_CENTRAL_CONFIG: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result[
|
||||
"data"
|
||||
] == SOURCE_CONFIG | TYPE_CONFIG | MOCK_TH_OVER_SWITCH_TPI_CONFIG | MOCK_PRESETS_CONFIG | MOCK_ADVANCED_CONFIG | {
|
||||
CONF_INVERSE_SWITCH: False
|
||||
}
|
||||
assert result["data"] == (
|
||||
SOURCE_CONFIG
|
||||
| MAIN_CONFIG
|
||||
| TYPE_CONFIG
|
||||
| {
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_TPI_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: False,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: False,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
}
|
||||
)
|
||||
assert result["result"]
|
||||
assert result["result"].domain == DOMAIN
|
||||
assert result["result"].version == 1
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# pylint: disable=unused-argument, line-too-long, protected-access
|
||||
""" Test the Window management """
|
||||
import asyncio
|
||||
# import asyncio
|
||||
import logging
|
||||
from unittest.mock import patch, call
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
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
|
||||
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
@@ -44,14 +47,14 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test
|
||||
CONF_INVERSE_SWITCH: True
|
||||
CONF_INVERSE_SWITCH: True,
|
||||
},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call, patch(
|
||||
"homeassistant.core.StateMachine.is_state", return_value=True # switch is On
|
||||
"homeassistant.core.StateMachine.is_state", return_value=True # switch is On
|
||||
):
|
||||
entity: ThermostatOverSwitch = await create_thermostat(
|
||||
hass, entry, "climate.theoverswitchmockname"
|
||||
@@ -80,7 +83,7 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
), patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call, patch(
|
||||
"homeassistant.core.StateMachine.is_state", return_value=True # switch is Off
|
||||
"homeassistant.core.StateMachine.is_state", return_value=True # switch is Off
|
||||
):
|
||||
event_timestamp = now - timedelta(minutes=4)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
@@ -90,9 +93,13 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
# not updated cause mocked assert entity.is_device_active is True
|
||||
|
||||
assert mock_service_call.call_count == 1
|
||||
mock_service_call.assert_has_calls([
|
||||
call.async_call('switch', SERVICE_TURN_OFF, {'entity_id': 'switch.mock_switch'}),
|
||||
])
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"switch", SERVICE_TURN_OFF, {"entity_id": "switch.mock_switch"}
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# 2. Make the temperature up to deactivate the switch
|
||||
with patch(
|
||||
@@ -100,7 +107,8 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
), patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call, patch(
|
||||
"homeassistant.core.StateMachine.is_state", return_value=False # switch is On -> it should turned off
|
||||
"homeassistant.core.StateMachine.is_state",
|
||||
return_value=False, # switch is On -> it should turned off
|
||||
):
|
||||
event_timestamp = now - timedelta(minutes=3)
|
||||
await send_temperature_change_event(entity, 25, event_timestamp)
|
||||
@@ -114,11 +122,13 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
await entity._underlyings[0].turn_off()
|
||||
|
||||
assert mock_service_call.call_count == 1
|
||||
mock_service_call.assert_has_calls([
|
||||
call.async_call('switch', SERVICE_TURN_ON, {'entity_id': 'switch.mock_switch'}),
|
||||
])
|
||||
|
||||
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"switch", SERVICE_TURN_ON, {"entity_id": "switch.mock_switch"}
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# Clean the entity
|
||||
entity.remove_thermostat()
|
||||
|
||||
@@ -472,7 +472,7 @@ async def test_multiple_climates_underlying_changes(
|
||||
skip_hass_states_is_state,
|
||||
skip_send_event,
|
||||
): # pylint: disable=unused-argument
|
||||
"""Test that when multiple switch are configured the activation of one underlying
|
||||
"""Test that when multiple climate are configured the activation of one underlying
|
||||
climate activate the others"""
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
@@ -541,11 +541,15 @@ async def test_multiple_climates_underlying_changes(
|
||||
assert entity.is_device_active is False # pylint: disable=protected-access
|
||||
|
||||
# Stop heating on one underlying climate
|
||||
# All underlying supposed to be aligned with the hvac_mode now
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.async_control_heating"
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
|
||||
) as mock_underlying_set_hvac_mode:
|
||||
) as mock_underlying_set_hvac_mode, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_mode",
|
||||
HVACMode.HEAT,
|
||||
):
|
||||
# Wait 11 sec so that the event will not be discarded
|
||||
event_timestamp = now + timedelta(seconds=11)
|
||||
await send_climate_change_event(
|
||||
@@ -555,6 +559,7 @@ async def test_multiple_climates_underlying_changes(
|
||||
HVACAction.OFF,
|
||||
HVACAction.HEATING,
|
||||
event_timestamp,
|
||||
underlying_entity_id="switch.mock_climate3",
|
||||
)
|
||||
|
||||
# Should be call for all Switch
|
||||
@@ -577,6 +582,9 @@ async def test_multiple_climates_underlying_changes(
|
||||
# a function but a property
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_action",
|
||||
HVACAction.IDLE,
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_mode",
|
||||
HVACMode.OFF,
|
||||
):
|
||||
# Wait 11 sec so that the event will not be discarded
|
||||
event_timestamp = now + timedelta(seconds=11)
|
||||
@@ -601,6 +609,113 @@ async def test_multiple_climates_underlying_changes(
|
||||
assert entity.is_device_active is False # pylint: disable=protected-access
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_multiple_climates_underlying_changes_not_aligned(
|
||||
hass: HomeAssistant,
|
||||
skip_hass_states_is_state,
|
||||
skip_send_event,
|
||||
): # pylint: disable=unused-argument
|
||||
"""Test that when multiple climate are configured the activation of one underlying
|
||||
climate don't activate the others if their havc_mode are not aligned"""
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOver4ClimateMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOver4ClimateMockName",
|
||||
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: 8,
|
||||
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: "switch.mock_climate1",
|
||||
CONF_CLIMATE_2: "switch.mock_climate2",
|
||||
CONF_CLIMATE_3: "switch.mock_climate3",
|
||||
CONF_CLIMATE_4: "switch.mock_climate4",
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
},
|
||||
)
|
||||
|
||||
entity: BaseThermostat = await create_thermostat(
|
||||
hass, entry, "climate.theover4climatemockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.is_over_climate is True
|
||||
assert entity.nb_underlying_entities == 4
|
||||
|
||||
# start heating, in boost mode. We block the control_heating to avoid running a cycle
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.async_control_heating"
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
|
||||
) as mock_underlying_set_hvac_mode:
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.preset_mode is PRESET_BOOST
|
||||
assert entity.target_temperature == 19
|
||||
assert entity.window_state is STATE_OFF
|
||||
|
||||
event_timestamp = now - timedelta(minutes=4)
|
||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||
|
||||
# Should be call for all Switch
|
||||
assert mock_underlying_set_hvac_mode.call_count == 4
|
||||
mock_underlying_set_hvac_mode.assert_has_calls(
|
||||
[
|
||||
call.set_hvac_mode(HVACMode.HEAT),
|
||||
]
|
||||
)
|
||||
|
||||
# Stop heating on one underlying climate
|
||||
# All underlying supposed to be aligned with the hvac_mode now
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.async_control_heating"
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
|
||||
) as mock_underlying_set_hvac_mode, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_mode",
|
||||
HVACMode.COOL,
|
||||
):
|
||||
# Wait 11 sec so that the event will not be discarded
|
||||
event_timestamp = now + timedelta(seconds=11)
|
||||
await send_climate_change_event(
|
||||
entity,
|
||||
HVACMode.OFF,
|
||||
HVACMode.HEAT,
|
||||
HVACAction.OFF,
|
||||
HVACAction.HEATING,
|
||||
event_timestamp,
|
||||
underlying_entity_id="switch.mock_climate3",
|
||||
)
|
||||
|
||||
# Should be call for all Switch
|
||||
assert mock_underlying_set_hvac_mode.call_count == 0
|
||||
# mock_underlying_set_hvac_mode.assert_has_calls(
|
||||
# [
|
||||
# call.set_hvac_mode(HVACMode.OFF),
|
||||
# ]
|
||||
# )
|
||||
# No change
|
||||
assert entity.hvac_mode == HVACMode.HEAT
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_multiple_switch_power_management(
|
||||
|
||||
@@ -288,7 +288,7 @@ async def test_security_feature_back_on_percent(
|
||||
assert entity.security_state is False
|
||||
assert mock_send_event.call_count == 0
|
||||
|
||||
# 3. Set security mode with a preset change
|
||||
# 3. Set safety mode with a preset change
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
@@ -400,7 +400,7 @@ async def test_security_over_climate(
|
||||
skip_turn_on_off_heater,
|
||||
skip_send_event,
|
||||
):
|
||||
"""Test that when a underlying climate is not available the VTherm doesn't go into security mode"""
|
||||
"""Test that when a underlying climate is not available the VTherm doesn't go into safety mode"""
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
@@ -471,7 +471,7 @@ async def test_security_over_climate(
|
||||
assert mock_find_climate.mock_calls[0] == call()
|
||||
mock_find_climate.assert_has_calls([call.find_underlying_entity()])
|
||||
|
||||
# Force security mode
|
||||
# Force safety mode
|
||||
assert entity._last_ext_temperature_measure is not None
|
||||
assert entity._last_temperature_measure is not None
|
||||
assert (
|
||||
@@ -505,7 +505,7 @@ async def test_security_over_climate(
|
||||
event_timestamp = now - timedelta(minutes=6)
|
||||
|
||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||
# Should stay False because a climate is never in security mode
|
||||
# Should stay False because a climate is never in safety mode
|
||||
assert entity.security_state is False
|
||||
assert entity.preset_mode == "none"
|
||||
assert entity._saved_preset_mode == "none"
|
||||
|
||||
@@ -129,7 +129,7 @@ async def test_over_switch_ac_full_start(
|
||||
|
||||
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 == 27 # eco_ac_away (no change)
|
||||
|
||||
# Close a window
|
||||
with patch("homeassistant.helpers.condition.state", return_value=True):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from unittest.mock import patch, call
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.components.climate import HVACAction, HVACMode
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
||||
@@ -214,20 +214,60 @@ async def test_over_valve_full_start(
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
|
||||
# Change internal temperature
|
||||
expected_state = State(
|
||||
entity_id="number.mock_valve", state="0", attributes={"min": 10, "max": 50}
|
||||
)
|
||||
|
||||
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=0
|
||||
"homeassistant.core.StateMachine.get", return_value=expected_state
|
||||
):
|
||||
event_timestamp = now - timedelta(minutes=3)
|
||||
await send_temperature_change_event(entity, 20, datetime.now())
|
||||
assert entity.valve_open_percent == 0
|
||||
assert entity.is_device_active is False
|
||||
assert entity.hvac_action == HVACAction.IDLE
|
||||
assert entity.is_device_active is True # Should be 0 but in fact 10 is send
|
||||
assert (
|
||||
entity.hvac_action == HVACAction.HEATING
|
||||
) # Should be IDLE but heating due to 10
|
||||
|
||||
assert mock_service_call.call_count == 1
|
||||
# The VTherm valve is 0, but the underlying have received 10 which is the min
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
{"entity_id": "number.mock_valve", "value": 10},
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
await send_temperature_change_event(entity, 17, datetime.now())
|
||||
assert mock_service_call.call_count == 2
|
||||
# The VTherm valve is 0, but the underlying have received 10 which is the min
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
{
|
||||
"entity_id": "number.mock_valve",
|
||||
"value": 10,
|
||||
}, # the min allowed value
|
||||
),
|
||||
call.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
{
|
||||
"entity_id": "number.mock_valve",
|
||||
"value": 50,
|
||||
}, # the max allowed value
|
||||
),
|
||||
]
|
||||
)
|
||||
# switch to Eco
|
||||
await entity.async_set_preset_mode(PRESET_ECO)
|
||||
assert entity.preset_mode is PRESET_ECO
|
||||
@@ -243,6 +283,18 @@ async def test_over_valve_full_start(
|
||||
assert entity.is_device_active is True
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
|
||||
# Test window open/close (with a normal min/max so that is_device_active is False when open_percent is 0)
|
||||
expected_state = State(
|
||||
entity_id="number.mock_valve", state="0", attributes={"min": 0, "max": 99}
|
||||
)
|
||||
|
||||
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=expected_state
|
||||
):
|
||||
# Open a window
|
||||
with patch("homeassistant.helpers.condition.state", return_value=True):
|
||||
event_timestamp = now - timedelta(minutes=1)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# pylint: disable=unused-argument, line-too-long, protected-access
|
||||
# pylint: disable=unused-argument, line-too-long, protected-access, too-many-lines
|
||||
""" Test the Window management """
|
||||
import asyncio
|
||||
import logging
|
||||
@@ -295,6 +295,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
assert entity.target_temperature == 21
|
||||
|
||||
assert entity.window_state is STATE_OFF
|
||||
assert entity.is_window_auto_enabled is True
|
||||
|
||||
# Initialize the slope algo with 2 measurements
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
@@ -431,6 +432,125 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_window_auto_fast_and_sensor(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test that the auto-window detection algorithm is deactivated if a window sensor is provided"""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverSwitchMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
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": 21,
|
||||
CONF_USE_WINDOW_FEATURE: True,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_HEATER: "switch.mock_switch",
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.01,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_WINDOW_SENSOR: "binary_sensor.fake_window_sensor",
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 10, # Should be 0 for test
|
||||
},
|
||||
)
|
||||
|
||||
entity: BaseThermostat = await create_thermostat(
|
||||
hass, entry, "climate.theoverswitchmockname"
|
||||
)
|
||||
assert entity
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now = datetime.now(tz)
|
||||
|
||||
tpi_algo = entity._prop_algorithm
|
||||
assert tpi_algo
|
||||
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.preset_mode is PRESET_BOOST
|
||||
assert entity.target_temperature == 21
|
||||
|
||||
assert entity.window_state is STATE_OFF
|
||||
assert entity.is_window_auto_enabled is False
|
||||
|
||||
# Initialize the slope algo with 2 measurements
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
|
||||
# Make the temperature down
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||
) as mock_heater_on, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
|
||||
) as mock_heater_off, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||
return_value=True,
|
||||
):
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
|
||||
# The heater don't turns on
|
||||
assert mock_send_event.call_count == 0
|
||||
assert entity.is_device_active is True
|
||||
assert entity.last_temperature_slope == 0.0
|
||||
assert entity._window_auto_algo.is_window_open_detected() is False
|
||||
assert entity._window_auto_algo.is_window_close_detected() is False
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
|
||||
# send one degre down in one minute
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||
) as mock_heater_on, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
|
||||
) as mock_heater_off, patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||
return_value=True,
|
||||
):
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 18, event_timestamp)
|
||||
|
||||
assert mock_send_event.call_count == 0
|
||||
assert mock_heater_on.call_count == 0 # no change in heater
|
||||
assert mock_heater_off.call_count == 0 # no change in heater
|
||||
assert entity.last_temperature_slope == -6.24
|
||||
# The window open should be detected (but not used)
|
||||
# because we need to calculate the slope anyway, we have the algorithm running
|
||||
assert entity._window_auto_algo.is_window_open_detected() is True
|
||||
assert entity._window_auto_algo.is_window_close_detected() is False
|
||||
assert entity.window_auto_state == STATE_OFF
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
|
||||
# Clean the entity
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
@@ -461,7 +581,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 6,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 1, # 0 will deactivate window auto detection
|
||||
},
|
||||
)
|
||||
|
||||
@@ -484,8 +604,9 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
assert entity.target_temperature == 21
|
||||
|
||||
assert entity.window_state is STATE_OFF
|
||||
assert entity.is_window_auto_enabled is True
|
||||
|
||||
# Initialize the slope algo with 2 measurements
|
||||
# 1. Initialize the slope algo with 2 measurements
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
@@ -493,7 +614,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
|
||||
# Make the temperature down
|
||||
# 2. Make the temperature down
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
@@ -513,7 +634,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
assert entity._window_auto_algo.is_window_close_detected() is False
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
|
||||
# send one degre down in one minute
|
||||
# 3. send one degre down in one minute
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
@@ -549,12 +670,14 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
assert entity.window_auto_state == STATE_ON
|
||||
assert entity.hvac_mode is HVACMode.OFF
|
||||
|
||||
# This is to avoid that the slope stayx under 6, else we will reactivate the window immediatly
|
||||
# 4. This is to avoid that the slope stay under 6, else we will reactivate the window immediatly
|
||||
event_timestamp = event_timestamp + timedelta(minutes=1)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp, sleep=False)
|
||||
dearm_window_auto = await send_temperature_change_event(
|
||||
entity, 19, event_timestamp, sleep=False
|
||||
)
|
||||
assert entity.last_temperature_slope > -6.0
|
||||
|
||||
# Waits for automatic disable
|
||||
# 5. Waits for automatic disable
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
) as mock_send_event, patch(
|
||||
@@ -563,7 +686,8 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||
return_value=False,
|
||||
):
|
||||
await asyncio.sleep(0.3)
|
||||
# simulate the expiration of the delay
|
||||
await dearm_window_auto(None)
|
||||
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.preset_mode is PRESET_BOOST
|
||||
@@ -751,6 +875,7 @@ async def test_window_bypass(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
assert entity.target_temperature == 19
|
||||
|
||||
assert entity.window_state is STATE_OFF
|
||||
assert entity.is_window_auto_enabled is False
|
||||
|
||||
# change temperature to force turning on the heater
|
||||
with patch(
|
||||
@@ -867,7 +992,7 @@ async def test_window_auto_bypass(hass: HomeAssistant, skip_hass_states_is_state
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 6,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 1, # Should be > 0 to activate window_auto
|
||||
},
|
||||
)
|
||||
|
||||
@@ -890,6 +1015,7 @@ async def test_window_auto_bypass(hass: HomeAssistant, skip_hass_states_is_state
|
||||
assert entity.target_temperature == 21
|
||||
|
||||
assert entity.window_state is STATE_OFF
|
||||
assert entity.is_window_auto_enabled
|
||||
|
||||
# Initialize the slope algo with 2 measurements
|
||||
event_timestamp = now + timedelta(minutes=1)
|
||||
|
||||
Reference in New Issue
Block a user