Compare commits
11 Commits
1.0.0beta1
...
1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
162b2d1a1b | ||
|
|
c762c91eb9 | ||
|
|
62425bb91f | ||
|
|
952d6d5e97 | ||
|
|
638e007c21 | ||
|
|
c520d7fad5 | ||
|
|
c366314a95 | ||
|
|
6223177918 | ||
|
|
2bd6b7093e | ||
|
|
eabcc64fb1 | ||
|
|
903d7d1709 |
500
README-fr.md
Normal file
@@ -0,0 +1,500 @@
|
||||
[![GitHub Release][releases-shield]][releases]
|
||||
[![GitHub Activity][commits-shield]][commits]
|
||||
[![License][license-shield]](LICENSE)
|
||||
[![hacs][hacs_badge]][hacs]
|
||||
|
||||

|
||||
|
||||
>  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. ;-).
|
||||
|
||||
- [Quand l'utiliser et ne pas l'utiliser](#quand-lutiliser-et-ne-pas-lutiliser)
|
||||
- [Pourquoi une nouvelle implémentation du thermostat ?](#pourquoi-une-nouvelle-implémentation-du-thermostat-)
|
||||
- [Comment installer cet incroyable Thermostat Versatile ?](#comment-installer-cet-incroyable-thermostat-versatile-)
|
||||
- [HACS installation (recommendé)](#hacs-installation-recommendé)
|
||||
- [Installation manuelle](#installation-manuelle)
|
||||
- [Configuration](#configuration)
|
||||
- [Configuration minimale](#configuration-minimale)
|
||||
- [Configurez les coefficients de l'algorithme TPI](#configurez-les-coefficients-de-lalgorithme-tpi)
|
||||
- [Configurer la température préréglée](#configurer-la-température-préréglée)
|
||||
- [Configurer les portes/fenêtres en allumant/éteignant les thermostats](#configurer-les-portesfenêtres-en-allumantéteignant-les-thermostats)
|
||||
- [Configurer le mode d'activité ou la détection de mouvement](#configurer-le-mode-dactivité-ou-la-détection-de-mouvement)
|
||||
- [Configurer la gestion de l'alimentation](#configurer-la-gestion-de-lalimentation)
|
||||
- [Configurer la présence ou l'occupation](#configurer-la-présence-ou-loccupation)
|
||||
- [Configuration avancée](#configuration-avancée)
|
||||
- [Exemples de réglage](#exemples-de-réglage)
|
||||
- [Chauffage électrique](#chauffage-électrique)
|
||||
- [Chauffage central (chauffage gaz ou fuel)](#chauffage-central-chauffage-gaz-ou-fuel)
|
||||
- [Le capteur de température sera alimenté par batterie](#le-capteur-de-température-sera-alimenté-par-batterie)
|
||||
- [Capteur de température réactif](#capteur-de-température-réactif)
|
||||
- [Ma configuration prédéfinie](#ma-configuration-prédéfinie)
|
||||
- [Algorithme](#algorithme)
|
||||
- [Algorithme TPI](#algorithme-tpi)
|
||||
- [Services](#services)
|
||||
- [Forcer la présence/occupation](#forcer-la-présenceoccupation)
|
||||
- [Modifier la température des préréglages](#modifier-la-température-des-préréglages)
|
||||
- [Attributs personnalisés](#attributs-personnalisés)
|
||||
- [Quelques résultats](#quelques-résultats)
|
||||
- [Encore mieux](#encore-mieux)
|
||||
- [Encore mieux avec le composant Scheduler !](#encore-mieux-avec-le-composant-scheduler-)
|
||||
- [Encore bien mieux avec la custom:simple-thermostat front integration](#encore-bien-mieux-avec-la-customsimple-thermostat-front-integration)
|
||||
- [Toujours mieux avec Apex-chart pour régler votre thermostat](#toujours-mieux-avec-apex-chart-pour-régler-votre-thermostat)
|
||||
- [Les contributions sont les bienvenues !](#les-contributions-sont-les-bienvenues)
|
||||
|
||||
|
||||
_Composant développé à l'aide de l'incroyable modèle de développement [[blueprint](https://github.com/custom-components/integration_blueprint)]._
|
||||
|
||||
Ce composant personnalisé pour Home Assistant est une mise à niveau et est une réécriture complète du composant "Awesome thermostat" (voir [Github](https://github.com/dadge/awesome_thermostat)) avec l'ajout de fonctionnalités.
|
||||
|
||||
# Quand l'utiliser et ne pas l'utiliser
|
||||
Ce thermostat a pour but de commander un radiateur qui ne fonctionne qu'en mode marche/arrêt. La configuration minimale nécessaire pour utiliser ce thermostat est :
|
||||
1. un équipement comme un radiateur (un interrupteur),
|
||||
2. une sonde de température pour la pièce (ou un input_number),
|
||||
3. un capteur de température externe (pensez à l'intégration météo si vous n'en avez pas)
|
||||
|
||||
Parce que cette intégration vise à commander le radiateur en tenant compte du préréglage configuré (preset) et de la température ambiante, ces informations sont obligatoires.
|
||||
|
||||
# Pourquoi une nouvelle implémentation du thermostat ?
|
||||
|
||||
Pour mon usage personnel, j'avais besoin d'ajouter quelques fonctionnalités et aussi de mettre à jour le comportement implémenté dans le composant précédent "Awesome thermostat".
|
||||
Ce composant nommé __Versatile thermostat__ gère les cas d'utilisation suivants :
|
||||
- Configuration via l'interface graphique d'intégration standard (à l'aide du flux Config Entry),
|
||||
- Utilisations complètes du **mode préréglages**,
|
||||
- Désactiver le mode préréglé lorsque la température est **définie manuellement** sur un thermostat,
|
||||
- Éteindre/allumer un thermostat lorsqu'une **porte ou des fenêtres sont ouvertes/fermées** après un certain délai,
|
||||
- Changer de preset lorsqu'une **activité est détectée** ou non dans une pièce pendant un temps défini,
|
||||
- Utiliser un algorithme **TPI (Time Proportional Interval)** grâce à l'algorithme [[Argonaute](https://forum.hacf.fr/u/argonaute/summary)] ,
|
||||
- Ajoutez 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é.
|
||||
- Ajouter 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.
|
||||
- Ajoutez 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.
|
||||
|
||||
# Comment installer cet incroyable Thermostat Versatile ?
|
||||
|
||||
## HACS installation (recommendé)
|
||||
|
||||
1. Installez [HACS](https://hacs.xyz/). De cette façon, vous obtenez automatiquement les mises à jour.
|
||||
2. Ajoutez ce repository Github en tant que repository personnalisé dans les paramètres HACS.
|
||||
3. recherchez et installez "Versatile Thermostat" dans HACS et cliquez sur "installer".
|
||||
4. Redémarrez Home Assistant.
|
||||
5. Ensuite, vous pouvez ajouter une intégration de Versatile Thermostat dans la page d'intégration. Vous ajoutez autant de thermostats dont vous avez besoin (généralement un par radiateur qui doit être géré ou par pompe dans le cas d'un chauffage centralisé)
|
||||
|
||||
|
||||
## Installation manuelle
|
||||
|
||||
1. À l'aide de l'outil de votre choix, ouvrez le répertoire (dossier) de votre configuration HA (où vous trouverez `configuration.yaml`).
|
||||
2. Si vous n'avez pas de répertoire (dossier) `custom_components`, vous devez le créer.
|
||||
3. Dans le répertoire (dossier) `custom_components`, créez un nouveau dossier appelé `versatile_thermostat`.
|
||||
4. Téléchargez _tous_ les fichiers du répertoire `custom_components/versatile_thermostat/` (dossier) dans ce référentiel.
|
||||
5. Placez les fichiers que vous avez téléchargés dans le nouveau répertoire (dossier) que vous avez créé.
|
||||
6. Redémarrez l'assistant domestique
|
||||
7. Configurer la nouvelle intégration du Versatile Thermostat
|
||||
|
||||
# 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.
|
||||
|
||||
Cliquez sur le bouton Ajouter une intégration dans la page d'intégration
|
||||
|
||||

|
||||
|
||||
La configuration peut être modifiée via la même interface. Sélectionnez simplement le thermostat à modifier, appuyez sur "Configurer" et vous pourrez modifier certains paramètres ou la configuration.
|
||||
|
||||
Suivez ensuite les étapes de configuration comme suit :
|
||||
|
||||
## Configuration minimale
|
||||
|
||||

|
||||
|
||||
Donnez les principaux attributs obligatoires :
|
||||
1. un nom (sera le nom de l'intégration et aussi le nom de l'entité climate)
|
||||
2. un identifiant d'entité d'équipement qui représente l'élément chauffant. Cet équipement doit pouvoir s'allumer ou s'éteindre,
|
||||
3. un identifiant d'entité de capteur de température qui donne la température de la pièce dans laquelle le radiateur est installé,
|
||||
4. une entité capteur de température donnant la température extérieure. Si vous n'avez pas de capteur externe, vous pouvez utiliser l'intégration météo locale
|
||||
5. 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),
|
||||
6. Algorithme à utiliser. Aujourd'hui, seul l'algorithme TPI est disponible. Voir [algorithme](#algorithme)
|
||||
|
||||
>  _*Notes*_
|
||||
1. 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**,
|
||||
2. si le cycle est trop court, le radiateur ne pourra jamais atteindre la température cible en effet pour le radiateur à accumulation et il sera sollicité inutilement
|
||||
|
||||
## Configurez les coefficients de l'algorithme TPI
|
||||
|
||||
Cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
Pour plus d'informations sur l'algorithme TPI et son réglage, veuillez vous référer à [algorithm](#algorithm).
|
||||
|
||||
## Configurer la température préréglée
|
||||
Cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
Le mode préréglé (preset) vous permet de préconfigurer la température ciblée. Utilisé en conjonction avec Scheduler (voir [scheduler](#even-better-with-scheduler-component) vous aurez un moyen puissant et simple d'optimiser la température par rapport à la consommation électrique de votre maison. Les préréglages gérés sont les suivants :
|
||||
- **Eco** : l'appareil est en mode d'économie d'énergie
|
||||
- **Confort** : l'appareil est en mode confort
|
||||
- **Boost** : l'appareil tourne toutes les vannes à fond
|
||||
|
||||
**Aucun** est toujours ajouté dans la liste des modes, car c'est un moyen de ne pas utiliser les preset mais une **température manuelle** à la place.
|
||||
|
||||
>  _*Notes*_
|
||||
1. En modifiant manuellement la température cible, réglez le préréglage sur Aucun (pas de préréglage). De cette façon, vous pouvez toujours définir une température cible même si aucun préréglage n'est disponible.
|
||||
2. Le préréglage standard ``Away`` est un préréglage caché qui n'est pas directement sélectionnable. Versatile Thermostat utilise la gestion de présence ou la gestion de mouvement pour régler automatiquement et dynamiquement la température cible en fonction d'une présence dans le logement ou d'une activité dans la pièce. Voir [gestion de la présence](#configure-the-presence-management).
|
||||
3. Si vous utilisez la gestion du délestage, vous verrez un préréglage caché nommé ``power``. Le préréglage de l'élément chauffant est réglé sur « puissance » lorsque des conditions de surpuissance sont rencontrées et que le délestage est actif pour cet élément chauffant. Voir [gestion de l'alimentation](#configure-the-power-management).
|
||||
4. si vous utilisez la configuration avancée, vous verrez le préréglage défini sur ``sécurité`` si la température n'a pas pu être récupérée après un certain délai
|
||||
5. Si vous ne souhaitez pas utiliser le préréglage, indiquez 0 comme température. Le préréglage sera alors ignoré et ne s'affichera pas dans le composant front
|
||||
|
||||
## Configurer les portes/fenêtres en allumant/éteignant les thermostats
|
||||
Cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
Donnez les attributs suivants :
|
||||
1. un identifiant d'entité d'un **capteur de fenêtre/porte**. Cela devrait être un binary_sensor ou un input_boolean. L'état de l'entité doit être 'on' lorsque la fenêtre est ouverte ou 'off' lorsqu'elle est fermée
|
||||
2. un **délai en secondes** avant tout changement. Cela permet d'ouvrir rapidement une fenêtre sans arrêter le chauffage.
|
||||
|
||||
Et c'est tout ! votre thermostat s'éteindra lorsque les fenêtres seront ouvertes et se rallumera lorsqu'il sera fermé après le délai.
|
||||
|
||||
>  _*Notes*_
|
||||
1. Si vous souhaitez utiliser **plusieurs capteurs de porte/fenêtre** pour automatiser votre thermostat, créez simplement un groupe avec le comportement habituel (https://www.home-assistant.io/integrations/binary_sensor.group/)
|
||||
2. Si vous n'avez pas de capteur de fenêtre/porte dans votre chambre, laissez simplement l'identifiant de l'entité du capteur vide
|
||||
|
||||
## Configurer le mode d'activité ou la détection de mouvement
|
||||
Cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
Nous allons maintenant voir comment configurer le nouveau mode Activité.
|
||||
Ce dont nous avons besoin:
|
||||
- un **capteur de mouvement**. ID d'entité d'un capteur de mouvement. Les états du capteur de mouvement doivent être « on » (mouvement détecté) ou « off » (aucun mouvement détecté)
|
||||
- une durée de **délai de mouvement** (en secondes) définissant combien de temps nous attendons la confirmation du mouvement avant de considérer le mouvement
|
||||
- un **préréglage de "mouvement" **. Nous utiliserons la température de ce préréglage lorsqu'une activité sera détectée.
|
||||
- un **préréglage "pas de mouvement"**. Nous utiliserons la température de ce deuxième préréglage lorsqu'aucune activité n'est détectée.
|
||||
|
||||
Alors imaginons que nous voulions avoir le comportement suivant :
|
||||
- nous avons une pièce avec un thermostat réglé en mode activité, le mode "mouvement" choisi est confort (21.5C), le mode "pas de mouvement" choisi est Eco (18.5C) et la temporisation du mouvement est de 5 min.
|
||||
- la pièce est vide depuis un moment (aucune activité détectée), la température de cette pièce est de 18,5 C
|
||||
- quelqu'un entre dans la pièce, une activité est détectée la température est fixée à 21,5 C
|
||||
- la personne quitte la chambre, au bout de 5 min la température est ramenée à 18,5 C
|
||||
|
||||
Pour que cela fonctionne, le thermostat climatique doit être en mode préréglé « Activité ».
|
||||
|
||||
>  _*Notes*_
|
||||
1. Sachez que comme pour les autres modes prédéfinis, ``Activity`` ne sera proposé que s'il est correctement configuré. En d'autres termes, les 4 clés de configuration doivent être définies si vous souhaitez voir l'activité dans l'interface de l'assistant domestique
|
||||
|
||||
## Configurer la gestion de l'alimentation
|
||||
|
||||
Cliquez sur 'Valider' sur la page précédente et vous arriverez ici :
|
||||
|
||||

|
||||
|
||||
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** 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.
|
||||
|
||||
>  _*Notes*_
|
||||
1. En cas de délestage, le radiateur est réglé sur le préréglage nommé ``power``. Il s'agit d'un préréglage caché, vous ne pouvez pas le sélectionner manuellement.
|
||||
2. Je l'utilise pour éviter de dépasser la limite de mon contrat d'électricité lorsqu'un véhicule électrique est en charge. Cela crée une sorte d'autorégulation.
|
||||
3. Gardez toujours une marge, car la puissance max peut être brièvement dépassée en attendant le calcul du prochain cycle typiquement ou par des équipements non régulés.
|
||||
4. Si vous ne souhaitez pas utiliser cette fonctionnalité, laissez simplement l'identifiant des entités vide
|
||||
|
||||
## Configurer la présence ou l'occupation
|
||||
Cette fonction vous permet de modifier dynamiquement la température de tous les préréglages du thermostat configurés lorsque personne n'est à la maison ou lorsque quelqu'un rentre à la maison. Pour cela, vous devez configurer la température qui sera utilisée pour chaque préréglage lorsque la présence est désactivée. Lorsque le capteur de présence s'éteint, ces températures seront utilisées. Lorsqu'il se rallume, la température "normale" configurée pour le préréglage est utilisée. Voir [gestion des préréglages](#configure-the-preset-temperature).
|
||||
Pour configurer la présence remplissez ce formulaire :
|
||||
|
||||

|
||||
|
||||
Pour cela, vous devez configurer :
|
||||
1. Un **capteur d'occupation** dont l'état doit être 'on' ou 'home' si quelqu'un est présent ou 'off' ou 'not_home' sinon,
|
||||
2. La **température utilisée en Eco** prédéfinie en cas d'absence,
|
||||
3. La **température utilisée en Confort** préréglée en cas d'absence,
|
||||
4. La **température utilisée en Boost** préréglée en cas d'absence
|
||||
|
||||
>  _*Notes*_
|
||||
1. le changement de température est immédiat et se répercute sur le volet avant. Le calcul prendra en compte la nouvelle température cible au prochain calcul du cycle,
|
||||
2. vous pouvez utiliser le capteur direct person.xxxx ou un groupe de capteurs de Home Assistant. Le capteur de présence gère les états ``on`` ou ``home`` comme présents et les états ``off`` ou ``not_home`` comme absents.
|
||||
|
||||
## Configuration avancée
|
||||
Ces paramètres permettent d'affiner le réglage du thermostat.
|
||||
Le formulaire de configuration avancée est le suivant :
|
||||
|
||||

|
||||
|
||||
Le premier délai (minimal_activation_delay_sec) en sec dans le délai minimum acceptable pour allumer le chauffage. Lorsque le calcul donne un délai de mise sous tension inférieur à cette valeur, le chauffage reste éteint.
|
||||
|
||||
Le deuxième délai (security_delay_min) est le délai maximal entre deux mesures de température avant de régler le préréglage sur ``security`` et d'éteindre le thermostat. Si le capteur de température ne donne plus de mesures de température, le thermostat et le radiateur s'éteindront après ce délai et le préréglage du thermostat sera réglé sur ``security``. Ceci est utile pour éviter une surchauffe si la batterie de votre capteur de température est trop faible.
|
||||
|
||||
Voir [exemple de réglages](#examples-tuning) pour avoir des exemples de réglage communs
|
||||
|
||||
>  _*Notes*_
|
||||
1. Le préréglage ``security`` est un préréglage caché. Vous ne pouvez pas le sélectionner manuellement ou par le service prédéfini,
|
||||
2. Lorsque le capteur de température viendra à vivre et renverra les températures, le préréglage sera restauré à sa valeur précédente,
|
||||
3. Attention, deux températures sont nécessaires : la température interne et la température externe et chacune doit donner la température, sinon le thermostat sera en préréglage "security".
|
||||
|
||||
# Exemples de réglage
|
||||
|
||||
## Chauffage électrique
|
||||
- cycle : entre 5 et 10 minutes,
|
||||
- minimal_activation_delay_sec : 30 secondes
|
||||
|
||||
## Chauffage central (chauffage gaz ou fuel)
|
||||
- cycle : entre 30 et 60 min,
|
||||
- minimal_activation_delay_sec : 300 secondes (à cause du temps de réponse)
|
||||
|
||||
## Le capteur de température sera alimenté par batterie
|
||||
- security_delay_min : 60 min (parce que ces capteurs sont paresseux)
|
||||
|
||||
## Capteur de température réactif
|
||||
- security_delay_min : 15 min
|
||||
|
||||
## Ma configuration prédéfinie
|
||||
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.
|
||||
``Éco`` : 17
|
||||
``Confort`` : 19
|
||||
``Boost`` : 20
|
||||
|
||||
Lorsque la présence est désactivée :
|
||||
``Éco`` : 16,5
|
||||
``Confort`` : 17
|
||||
``Boost`` : 18
|
||||
|
||||
Le détecteur de mouvement de mon bureau est configuré pour utiliser ``Boost`` lorsqu'un mouvement est détecté et ``Eco`` sinon.
|
||||
|
||||
# Algorithme
|
||||
Cette intégration utilise un algorithme proportionnel. Un algorithme proportionnel est utile pour éviter l'oscillation autour de la température cible. Cet algorithme est basé sur un cycle qui alterne le chauffage et l'arrêt du chauffage. La proportion de chauffage par rapport à l'absence de chauffage est déterminée par la différence entre la température et la température cible. Plus grande est la différence et plus grande est la proportion de chauffage à l'intérieur du cycle.
|
||||
|
||||
Cet algorithme fait converger la température et arrête d'osciller.
|
||||
|
||||
## Algorithme TPI
|
||||
L'algorithme TPI consiste à calculer à chaque cycle un pourcentage d'état On vs Off pour le radiateur en utilisant la température cible, la température actuelle dans la pièce et la température extérieure actuelle.
|
||||
|
||||
Le pourcentage est calculé avec cette formule :
|
||||
|
||||
on_percent = coef_int * (température cible - température actuelle) + coef_ext * (température cible - température extérieure)
|
||||
Ensuite, faites 0 <= on_percent <= 1
|
||||
|
||||
Les valeurs par défaut pour coef_int et coef_ext sont respectivement : ``0.6`` et ``0.01``. Ces valeurs par défaut conviennent à une pièce standard bien isolée.
|
||||
|
||||
Pour régler ces coefficients, gardez à l'esprit que :
|
||||
1. **si la température cible n'est pas atteinte** après une situation stable, vous devez augmenter le ``coef_ext`` (le ``on_percent`` est trop élevé),
|
||||
2. **si la température cible est dépassée** après une situation stable, vous devez diminuer le ``coef_ext`` (le ``on_percent`` est trop bas),
|
||||
3. **si l'atteinte de la température cible est trop lente**, vous pouvez augmenter le ``coef_int`` pour donner plus de puissance au réchauffeur,
|
||||
4. **si l'atteinte de la température cible est trop rapide et que des oscillations apparaissent** autour de la cible, vous pouvez diminuer le ``coef_int`` pour donner moins de puissance au radiateur
|
||||
|
||||
Voir quelques situations à [examples](#some-results).
|
||||
|
||||
# Services
|
||||
|
||||
Cette implémentation personnalisée offre des services spécifiques pour faciliter l'intégration avec d'autres composants Home Assistant.
|
||||
|
||||
## Forcer la présence/occupation
|
||||
Ce service permet de forcer l'état de présence indépendamment du capteur de présence. Cela peut être utile si vous souhaitez gérer la présence via un service et non via un capteur. Par exemple, vous pouvez utiliser votre réveil pour forcer l'absence lorsqu'il est allumé.
|
||||
|
||||
Le code pour appeler ce service est le suivant :
|
||||
```
|
||||
service : thermostat_polyvalent.set_presence
|
||||
Les données:
|
||||
présence : "off"
|
||||
cible:
|
||||
entity_id : climate.my_thermostat
|
||||
```
|
||||
|
||||
## Modifier la température des préréglages
|
||||
Ce service est utile si vous souhaitez modifier dynamiquement la température préréglée. Au lieu de changer de préréglage, certains cas d'utilisation doivent modifier la température du préréglage. Ainsi, vous pouvez garder le Programmateur inchangé pour gérer le préréglage et ajuster la température du préréglage.
|
||||
Si le préréglage modifié est actuellement sélectionné, la modification de la température cible est immédiate et sera prise en compte au prochain cycle de calcul.
|
||||
|
||||
Vous pouvez modifier l'une ou les deux températures (lorsqu'elles sont présentes ou absentes) de chaque préréglage.
|
||||
|
||||
Utilisez le code suivant pour régler la température du préréglage :
|
||||
```
|
||||
service : thermostat_polyvalent.set_preset_temperature
|
||||
date:
|
||||
prest : boost
|
||||
temperature : 17,8
|
||||
temperature_away : 15
|
||||
target:
|
||||
entity_id : climate.my_thermostat
|
||||
```
|
||||
|
||||
>  _*Notes*_
|
||||
- après un redémarrage, les préréglages sont réinitialisés à la température configurée. Si vous souhaitez que votre changement soit permanent, vous devez modifier le préréglage de la température dans la configuration de l'intégration.
|
||||
|
||||
# Attributs personnalisés
|
||||
|
||||
Pour régler l'algorithme, vous avez accès à tout le contexte vu et calculé par le thermostat via des attributs dédiés. Vous pouvez voir (et utiliser) ces attributs dans l'IHM "Outils de développement / états" de HA. Entrez votre thermostat et vous verrez quelque chose comme ceci :
|
||||

|
||||
|
||||
Les attributs personnalisés sont les suivants :
|
||||
|
||||
| Attribut | Signification |
|
||||
| ----------| --------|
|
||||
| ``hvac_modes`` | La liste des modes supportés par le thermostat |
|
||||
| ``temp_min`` | La température minimale |
|
||||
| ``temp_max`` | La température maximale |
|
||||
| ``preset_modes`` | Les préréglages visibles pour ce thermostat. Les préréglages cachés ne sont pas affichés ici |
|
||||
| ``temperature_actuelle`` | La température actuelle telle que rapportée par le capteur |
|
||||
| ``temperature`` | La température cible |
|
||||
| ``action_hvac`` | L'action en cours d'exécution par le réchauffeur. Peut être inactif, chauffage |
|
||||
| ``preset_mode`` | Le préréglage actuellement sélectionné. Peut être l'un des 'preset_modes' ou un préréglage caché comme power |
|
||||
| ``[eco/confort/boost]_temp`` | La température configurée pour le préréglage xxx |
|
||||
| ``[eco/confort/boost]_away_temp`` | La température configurée pour le préréglage xxx lorsque la présence est désactivée ou not_home |
|
||||
| ``temp_power`` | La température utilisée lors de la détection de la perte |
|
||||
| ``on_percent`` | Le pourcentage sur calculé par l'algorithme TPI |
|
||||
| ``on_time_sec`` | La période On en sec. Doit être ```on_percent * cycle_min``` |
|
||||
| ``off_time_sec`` | La période d'arrêt en sec. Doit être ```(1 - on_percent) * cycle_min``` |
|
||||
| ``cycle_min`` | Le cycle de calcul en minutes |
|
||||
| ``function`` | L'algorithme utilisé pour le calcul du cycle |
|
||||
| ``tpi_coef_int`` | Le ``coef_int`` de l'algorithme TPI |
|
||||
| ``tpi_coef_ext`` | Le ``coef_ext`` de l'algorithme TPI |
|
||||
| ``saved_preset_mode`` | Le dernier preset utilisé avant le basculement automatique du preset |
|
||||
| ``saved_target_temp`` | La dernière température utilisée avant la commutation automatique |
|
||||
| ``window_state`` | Le dernier état connu du capteur de fenêtre. Aucun si la fenêtre n'est pas configurée |
|
||||
| ``motion_state`` | Le dernier état connu du capteur de mouvement. Aucun si le mouvement n'est pas configuré |
|
||||
| ``overpowering_state`` | Le dernier état connu du capteur surpuissant. Aucun si la gestion de l'alimentation n'est pas configurée |
|
||||
| ``presence_state`` | Le dernier état connu du capteur de présence. Aucun si la gestion de présence n'est pas configurée |
|
||||
| ``delay_security_min`` | Le délai avant de régler le mode de sécurité lorsque le capteur de température est éteint |
|
||||
| ``last_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température interne |
|
||||
| ``last_ext_temperature_datetime`` | La date et l'heure au format ISO8866 de la dernière réception de température extérieure |
|
||||
| ``**état_sécurité**`` | L'état de sécurité. vrai ou faux |
|
||||
| ``minimal_activation_delay_sec`` | Le délai d'activation minimal en secondes |
|
||||
| ``last_update_datetime`` | La date et l'heure au format ISO8866 de cet état |
|
||||
| ``friendly_name`` | Le nom du thermostat |
|
||||
| ``supported_features`` | Une combinaison de toutes les fonctionnalités prises en charge par ce thermostat. Voir la documentation officielle sur l'intégration climatique pour plus d'informations |
|
||||
|
||||
# Quelques résultats
|
||||
|
||||
**Convergence de la température vers la cible configurée par preset:**
|
||||

|
||||
|
||||
[Cycle de marche/arrêt calculé par l'intégration :](https://)
|
||||

|
||||
|
||||
**Coef_int trop élevé (oscillations autour de la cible)**
|
||||

|
||||
|
||||
**Évolution du calcul de l'algorithme**
|
||||

|
||||
Voir le code de ce composant [[ci-dessous](#even-better-with-apex-chart-to-tune-your-thermostat)]
|
||||
|
||||
**Thermostat finement réglé**
|
||||
Merci [impuR_Shozz](https://forum.hacf.fr/u/impur_shozz/summary) !
|
||||
On peut voir une stabilité autour de la température cible (consigne) et lorsqu'à cible le on_percent (puissance) est proche de 0,3 ce qui semble une très bonne valeur.
|
||||
|
||||

|
||||
|
||||
Enjoy !
|
||||
|
||||
# Encore mieux
|
||||
|
||||
## Encore mieux avec le composant Scheduler !
|
||||
|
||||
Afin de profiter de toute la puissance du Versatile Thermostat, je vous invite à l'utiliser avec https://github.com/nielsfaber/scheduler-component
|
||||
En effet, le composant scheduler propose une gestion de la base climatique sur les modes prédéfinis. Cette fonctionnalité a un intérêt limité avec le thermostat générique mais elle devient très puissante avec le thermostat Awesome :
|
||||
|
||||
À partir d'ici, je suppose que vous avez installé Awesome Thermostat et Scheduler Component.
|
||||
|
||||
Dans Scheduler, ajoutez un planning :
|
||||
|
||||

|
||||
|
||||
Choisissez le groupe "climat", choisissez une (ou plusieurs) entité(s), sélectionnez "MAKE SCHEME" et cliquez sur suivant :
|
||||
(il est possible de choisir "SET PRESET", mais je préfère utiliser "MAKE SCHEME")
|
||||
|
||||

|
||||
|
||||
Définissez votre schéma de mode et enregistrez :
|
||||
|
||||
|
||||

|
||||
|
||||
Dans cet exemple, j'ai réglé le mode ECO pendant la nuit et le jour lorsqu'il n'y a personne à la maison BOOST le matin et CONFORT le soir.
|
||||
|
||||
|
||||
J'espère que cet exemple vous aidera, n'hésitez pas à me faire part de vos retours !
|
||||
|
||||
## Encore bien mieux avec la custom:simple-thermostat front integration
|
||||
Le ``custom:simple-thermostat`` [ici](https://github.com/nervetattoo/simple-thermostat) est une excellente intégration qui permet une certaine personnalisation qui s'adapte bien à ce thermostat.
|
||||
Vous pouvez avoir quelque chose comme ça très facilement 
|
||||
Exemple de configuration :
|
||||
|
||||
```
|
||||
type: custom:simple-thermostat
|
||||
entity: climate.thermostat_sam2
|
||||
layout:
|
||||
step: row
|
||||
label:
|
||||
temperature: T°
|
||||
state: Etat
|
||||
hide:
|
||||
state: false
|
||||
control:
|
||||
hvac:
|
||||
_name: Mode
|
||||
preset:
|
||||
_name: Preset
|
||||
sensors:
|
||||
- entity: sensor.total_puissance_radiateur_sam2
|
||||
icon: mdi:lightning-bolt-outline
|
||||
header:
|
||||
toggle:
|
||||
entity: input_boolean.etat_ouverture_porte_sam
|
||||
name: Porte sam
|
||||
```
|
||||
|
||||
## Toujours mieux avec Apex-chart pour régler votre thermostat
|
||||
Vous pouvez obtenir une courbe comme celle présentée dans [some results](#some-results) avec une sorte de configuration de graphique Apex uniquement en utilisant les attributs personnalisés du thermostat décrits [ici](#custom-attributes) :
|
||||
|
||||
```
|
||||
type: custom:apexcharts-card
|
||||
header:
|
||||
show: true
|
||||
title: Tuning chauffage
|
||||
show_states: true
|
||||
colorize_states: true
|
||||
update_interval: 60sec
|
||||
graph_span: 4h
|
||||
yaxis:
|
||||
- id: left
|
||||
show: true
|
||||
decimals: 2
|
||||
- id: right
|
||||
decimals: 2
|
||||
show: true
|
||||
opposite: true
|
||||
series:
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: temperature
|
||||
type: line
|
||||
name: Target temp
|
||||
curve: smooth
|
||||
yaxis_id: left
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: current_temperature
|
||||
name: Current temp
|
||||
curve: smooth
|
||||
yaxis_id: left
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: on_percent
|
||||
name: Power percent
|
||||
curve: stepline
|
||||
yaxis_id: right
|
||||
```
|
||||
|
||||
# Les contributions sont les bienvenues !
|
||||
|
||||
Si vous souhaitez contribuer, veuillez lire les [directives de contribution](CONTRIBUTING.md)
|
||||
|
||||
***
|
||||
|
||||
[integration_blueprint]: https://github.com/custom-components/integration_blueprint
|
||||
[versatile_thermostat]: https://github.com/jmcollin78/versatile_thermostat
|
||||
[commits-shield]: https://img.shields.io/github/commit-activity/y/jmcollin78/versatile_thermostat.svg?style=for-the-badge
|
||||
[commits]: https://github.com/jmcollin78/versatile_thermostat/commits/master
|
||||
[hacs]: https://github.com/custom-components/hacs
|
||||
[hacs_badge]: https://img.shields.io/badge/HACS-Custom-41BDF5.svg?style=for-the-badge
|
||||
[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
|
||||
[forum]: https://community.home-assistant.io/
|
||||
[license-shield]: https://img.shields.io/github/license/jmcollin78/versatile_thermostat.svg?style=for-the-badge
|
||||
[maintenance-shield]: https://img.shields.io/badge/maintainer-Joakim%20Sørensen%20%40ludeeus-blue.svg?style=for-the-badge
|
||||
[releases-shield]: https://img.shields.io/github/release/jmcollin78/versatile_thermostat.svg?style=for-the-badge
|
||||
[releases]: https://github.com/jmcollin78/versatile_thermostat/releases
|
||||
411
README.md
@@ -3,12 +3,49 @@
|
||||
[![License][license-shield]](LICENSE)
|
||||
[![hacs][hacs_badge]][hacs]
|
||||
|
||||

|
||||
|
||||
_Component developed by using the amazing development template [blueprint][blueprint]._
|
||||
>  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 ;-).
|
||||
|
||||
- [When to use / not use](#when-to-use--not-use)
|
||||
- [Why another thermostat implementation ?](#why-another-thermostat-implementation-)
|
||||
- [How to install this incredible Versatile Thermostat ?](#how-to-install-this-incredible-versatile-thermostat-)
|
||||
- [HACS installation (recommended)](#hacs-installation-recommended)
|
||||
- [Manual installation](#manual-installation)
|
||||
- [Configuration](#configuration)
|
||||
- [Minimal configuration update](#minimal-configuration-update)
|
||||
- [Configure the TPI algorithm coefficients](#configure-the-tpi-algorithm-coefficients)
|
||||
- [Configure the preset temperature](#configure-the-preset-temperature)
|
||||
- [Configure the doors/windows turning on/off the thermostats](#configure-the-doorswindows-turning-onoff-the-thermostats)
|
||||
- [Configure the activity mode or motion detection](#configure-the-activity-mode-or-motion-detection)
|
||||
- [Configure the power management](#configure-the-power-management)
|
||||
- [Configure the presence or occupancy](#configure-the-presence-or-occupancy)
|
||||
- [Advanced configuration](#advanced-configuration)
|
||||
- [Examples tuning](#examples-tuning)
|
||||
- [Electrical heater](#electrical-heater)
|
||||
- [Central heating (gaz or fuel heating system)](#central-heating-gaz-or-fuel-heating-system)
|
||||
- [Temperature sensor will battery](#temperature-sensor-will-battery)
|
||||
- [Reponsive temperature sensor](#reponsive-temperature-sensor)
|
||||
- [My preset configuration](#my-preset-configuration)
|
||||
- [Algorithm](#algorithm)
|
||||
- [TPI algorithm](#tpi-algorithm)
|
||||
- [Services](#services)
|
||||
- [Force the presence / occupancy](#force-the-presence--occupancy)
|
||||
- [Change the temperature of presets](#change-the-temperature-of-presets)
|
||||
- [Custom attributes](#custom-attributes)
|
||||
- [Some results](#some-results)
|
||||
- [Even better](#even-better)
|
||||
- [Even Better with Scheduler Component !](#even-better-with-scheduler-component-)
|
||||
- [Even-even better with custom:simple-thermostat front integration](#even-even-better-with-customsimple-thermostat-front-integration)
|
||||
- [Even better with Apex-chart to tune your Thermostat](#even-better-with-apex-chart-to-tune-your-thermostat)
|
||||
- [Contributions are welcome!](#contributions-are-welcome)
|
||||
|
||||
|
||||
_Component developed by using the amazing development template [[blueprint](https://github.com/custom-components/integration_blueprint)]._
|
||||
|
||||
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.
|
||||
|
||||
## When to use / not use
|
||||
# When to use / not use
|
||||
This thermostat aims to command a heater which works only in on/off mode. The minimal needed configuration to use this thermostat is:
|
||||
1. an equipement like a heater (a switch),
|
||||
2. a temperature sensor for the room (or an input_number),
|
||||
@@ -16,20 +53,23 @@ This thermostat aims to command a heater which works only in on/off mode. The mi
|
||||
|
||||
Because this integration aims to command the heater considering the preset configured and the room temperature, those informations are mandatory.
|
||||
|
||||
## Why another thermostat implementation ?
|
||||
For my personnal usage, I needed to add a couple of features and also to update the behavior that I implemented in my previous component "Awesome thermostat".
|
||||
This new component "Versatile thermostat" now manage the following use cases :
|
||||
- Configuration through GUI using Config Entry flow,
|
||||
- Explicitely define the temperature for all presets mode,
|
||||
- Unset the preset mode when the temperature is manually defined on a thermostat,
|
||||
- Turn off/on a thermostat when a door or windows is opened/closed after a certain delay,
|
||||
- Set a preset when an activity is detected in a room, and another one after no activity has been detected for a defined time,
|
||||
- Use a proportional algorithm with two function (see below),
|
||||
- Add power management to avoid exceeding a defined total power. When max power is exceeded, a new 'power' preset is set on the climate entity. When power goes below the max, the previous preset is restored.
|
||||
# Why another thermostat implementation ?
|
||||
|
||||
## How to install this incredible thermostat
|
||||
For my personnal usage, I needed to add a couple of features and also to update the behavior that implemented in the previous component "Awesome thermostat".
|
||||
This component named __Versatile thermostat__ manage the following use cases :
|
||||
- Configuration through standard integration GUI (using Config Entry flow),
|
||||
- Full uses of **presets mode**,
|
||||
- Unset the preset mode when the temperature is **manually defined** on a thermostat,
|
||||
- Turn off/on a thermostat when a **door or windows is opened/closed** after a certain delay,
|
||||
- Change preset when an **activity is detected** or not in a room for a defined time,
|
||||
- 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.
|
||||
|
||||
### HACS installation
|
||||
# How to install this incredible Versatile Thermostat ?
|
||||
|
||||
## HACS installation (recommended)
|
||||
|
||||
1. Install [HACS](https://hacs.xyz/). That way you get updates automatically.
|
||||
2. Add this Github repository as custom repository in HACS settings.
|
||||
@@ -37,7 +77,7 @@ This new component "Versatile thermostat" now manage the following use cases :
|
||||
4. Restart Home Assistant,
|
||||
5. Then you can add an Versatile Thermostat integration in the integration page. You add as many Versatile Thermostat that you need (typically one per heater that should be managed)
|
||||
|
||||
### Manual installation
|
||||
## Manual installation
|
||||
|
||||
1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`).
|
||||
2. If you do not have a `custom_components` directory (folder) there, you need to create it.
|
||||
@@ -47,68 +87,81 @@ This new component "Versatile thermostat" now manage the following use cases :
|
||||
6. Restart Home Assistant
|
||||
7. Configure new Versatile Thermostat integration
|
||||
|
||||
## Minimum requirements
|
||||
|
||||
* This implementation can override or superseed the core generic thermostat
|
||||
# Configuration
|
||||
|
||||
## Configuration
|
||||
Note: no configuration in configuration.yaml is needed because all configuration is done through the standard GUI when adding the integration.
|
||||
|
||||
No configuration in configuration.yaml is needed because all configuration is done through the standard GUI when adding the integration.
|
||||
Click on Add integration button in the integration page
|
||||

|
||||

|
||||
|
||||
Follow the configurations steps as follow:
|
||||
The configuration can be change through the same interface. Simply select the thermostat to change, hit "Configure" and you will be able to change some parameters or configuration.
|
||||
|
||||
### Minimal configuration update
|
||||

|
||||
Then follow the configurations steps as follow:
|
||||
|
||||
## Minimal configuration update
|
||||

|
||||
|
||||
Give the main mandatory attributes:
|
||||
1. a name (will be the integration name and also the climate entity name)
|
||||
2. an equipment entity id which represent the heater. This equipment should be able to switch on or off,
|
||||
3. a sensor entity id which gives the temperature of the room in which the heater is installed,
|
||||
4. a cycle duration in minutes. At each cycle, the heater will be turned on then off for a calculated period in order to reach the targeted temperature (see presents below)
|
||||
5. a function used by the algorithm. 'linear' is the most common function. 'atan' is more aggressive and the targeted temperature will be reach sooner (but the power consumption is greater). Use it for room badly isolated,
|
||||
6. a bias value of type float. Proportional algorithm are known to never reach the targeted temperature. Depending of the room and heater configuration set a bias to reach the target. To evaluate the correct value, set it to 0, set the preset to a target temperature and see the current temperature reach. If it is below the target temperature, set the bias accordingly.
|
||||
3. a temporature sensor entity id which gives the temperature of the room in which the heater is installed,
|
||||
4. a temperature sensor entity giving the external temperature. If don't have any external sensor, you can use the local meteo integration
|
||||
5. a cycle duration in minutes. At each cycle, the heater will be turned on then off for a calculated period in order to reach the targeted temperature (see [preset](#configure-the-preset-temperature) below),
|
||||
6. Algorithm to use. Today only the TPI algorithm is available. See [algorithm](#algorithm)
|
||||
|
||||
### Configure the preset temperature
|
||||
>  _*Notes*_
|
||||
1. 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**,
|
||||
2. if the cycle is too short, the heater could never reach the target temperature indeed for heater with accumulation features and it will be unnecessary solicited
|
||||
|
||||
## Configure the TPI algorithm coefficients
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||

|
||||

|
||||
|
||||
Concerning the preset modes, you first have to know that, as defined in the core development documentation (https://developers.home-assistant.io/docs/core/entity/climate/), the preset mode handled are the following :
|
||||
- ECO : Device is running an energy-saving mode
|
||||
- AWAY : Device is in away mode
|
||||
- BOOST : Device turn all valve full up
|
||||
- COMFORT : Device is in comfort mode
|
||||
- POWER : An extra preset used when the power management detects an overpowering situation
|
||||
For more informations on the TPI algorithm and tuned please refer to [algorithm](#algorithm).
|
||||
|
||||
'None' is always added in the list of modes, as it is a way to not use the presets modes but a manual temperature instead.
|
||||
|
||||
!!! IMPORTANT !!! 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.
|
||||
|
||||
### Configure the doors/windows turning on/off the thermostats
|
||||
## Configure the preset temperature
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||

|
||||

|
||||
|
||||
The preset mode allows you to pre-configurate targeted temperature. Used in conjonction with Scheduler (see [scheduler](#even-better-with-scheduler-component) you will have a powerfull and simple way to optimize the temperature vs electrical consumption of your hous. Preset handled are the following :
|
||||
- **Eco** : device is running an energy-saving mode
|
||||
- **Comfort** : device is in comfort mode
|
||||
- **Boost** : device turn all valve full up
|
||||
|
||||
**None** is always added in the list of modes, as it is a way to not use the presets modes but a **manual temperature** instead.
|
||||
|
||||
>  _*Notes*_
|
||||
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
|
||||
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
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||

|
||||
|
||||
Give the following attributes:
|
||||
1. an entity id of a window/door sensor. This should be a binary_sensor or a input_boolean. The state of the entity should be 'on' or 'off'
|
||||
2. a delay in secondes before any change. This allow to quickly open a window without stopping the heater.
|
||||
1. an entity id of a **window/door sensor**. This should be a binary_sensor or a input_boolean. The state of the entity should be 'on' when the window is open or 'off' when closed
|
||||
2. a **delay in seconds** before any change. This allow to quickly open a window without stopping the heater.
|
||||
|
||||
And that's it ! your thermostat will turn off when the windows is open and be turned back on when it's closed afer the delay.
|
||||
|
||||
Note 1 : this implementation is based on 'normal' door/windows behavior, that's mean it considers it's closed when the state is 'off' and open when the state is 'on'
|
||||
>  _*Notes*_
|
||||
1. If you want to use **several door/windows sensors** to automatize your thermostat, just create a group with the regular behavior (https://www.home-assistant.io/integrations/binary_sensor.group/)
|
||||
2. If you don't have any window/door sensor in your room, just leave the sensor entity id empty
|
||||
|
||||
Note 2 : If you want to use several door/windows sensors to automatize your thermostat, just create a group with the regular behavior (https://www.home-assistant.io/integrations/binary_sensor.group/).
|
||||
|
||||
### Configure the activity mode or motion detection
|
||||
## Configure the activity mode or motion detection
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||

|
||||

|
||||
|
||||
We will now see how to configure the new Activity mode.
|
||||
What we need:
|
||||
- a motion sensor. The entity id of a motion sensor. Motion sensor states should be 'on' (motion detected) or 'off' (no motion detected)
|
||||
- a "motion delay" duration defining how many time we leave the temperature like in "motion" mode after the last motion is detected.
|
||||
- a target "motion" preset. We will used the same temperature than this preset when an activity is detected.
|
||||
- a target "no motion" preset. We will used the same temperature than this preset when no activity is detected.
|
||||
- a **motion sensor**. The entity id of a motion sensor. Motion sensor states should be 'on' (motion detected) or 'off' (no motion detected)
|
||||
- a **motion delay** (in seconds) duration defining how long we wait for motion confirmation before considering the motion
|
||||
- a **target "motion" preset**. We will used the temperature of this preset when an activity is detected.
|
||||
- a **target "no motion" preset**. We will used the temperature of this second preset when no activity is detected.
|
||||
|
||||
So imagine we want to have the following behavior :
|
||||
- we have room with a thermostat set in activity mode, the "motion" mode chosen is comfort (21.5C), the "no motion" mode chosen is Eco (18.5 C) and the motion delay is 5 min.
|
||||
@@ -116,37 +169,216 @@ So imagine we want to have the following behavior :
|
||||
- somebody enters into the room, an activity is detected the temperature is set to 21.5 C
|
||||
- the person leaves the room, after 5 min the temperature is set back to 18.5 C
|
||||
|
||||
For this to work, the climate thermostat should be in 'activity' preset mode.
|
||||
For this to work, the climate thermostat should be in ``Activity`` preset mode.
|
||||
|
||||
Be aware that as for the others preset modes, Activity will only be proposed if it's correctly configure. In other words, the 4 configuration keys have to be set if you want to see Activity in home assistant Interface
|
||||
>  _*Notes*_
|
||||
1. Be aware that as for the others preset modes, ``Activity`` will only be proposed if it's correctly configure. In other words, the 4 configuration keys have to be set if you want to see Activity in home assistant Interface
|
||||
|
||||
### Configure the power management
|
||||
This feature allows you to regulate the power consumption of your radiators. 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 radiator and the algorithm will not start a radiator if the max power will be exceeded after radiator starts.
|
||||
## Configure the power management
|
||||
|
||||
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** 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).
|
||||
This allows you to change the max power along time using a Sceduler or whatever you like.
|
||||
This allows you to change the max power along time using a Scheduler or whatever you like.
|
||||
|
||||
>  _*Notes*_
|
||||
1. When shedding is encountered, the heater is set to the preset named ``power``. This is a hidden preset, you cannot select it manually.
|
||||
2. I use this to avoid exceeded the limit of my electrical power contract when an electrical vehicle is charging. This makes a kind of auto-regulation.
|
||||
3. Always keep a margin, because max power can be briefly exceeded while waiting for the next cycle calculation typically or by not regulated equipement.
|
||||
4. If you don't want to use this feature, just leave the entities id empty
|
||||
|
||||
## Algorithm
|
||||
## Configure the presence or occupancy
|
||||
This feature allows you to dynamically changes the temperature of all configured Versatile thermostat's presets when nobody is at home or when someone comes back home. For this, you have to configure the temperature that will be used for each preset when presence is off. When the occupancy sensor turns to off, those tempoeratures will be used. When it turns on again the "normal" temperature configured for the preset is used. See [preset management](#configure-the-preset-temperature).
|
||||
To configure presence fills this form:
|
||||
|
||||

|
||||
|
||||
For this you need to configure:
|
||||
1. A **occupancy sensor** which state should be 'on' or 'home' if someone is present or 'off' or 'not_home' else,
|
||||
2. The **temperature used in Eco** preset when absent,
|
||||
3. The **temperature used in Comfort** preset when absent,
|
||||
4. The **temperature used in Boost** preset when absent
|
||||
|
||||
>  _*Notes*_
|
||||
1. the switch of temperature is immediate and is reflected on the front component. The calculation will take the new target temperature into account at the next cycle calculation,
|
||||
2. you can use direct person.xxxx sensor or group of sensors of Home Assistant. The presence sensor handles ``on`` or ``home`` states as present and ``off`` or ``not_home`` state as absent.
|
||||
|
||||
## Advanced configuration
|
||||
Those parameters allows to fine tune the thermostat.
|
||||
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.
|
||||
|
||||
See [exemple tuning](#examples-tuning) to have some commons tuning examples
|
||||
|
||||
>  _*Notes*_
|
||||
1. The ``security`` preset is a hidden preset. You cannot select it manually or by the preset service,
|
||||
2. When the temperature sensor will comes to live and re-send temperatures, the preset will be restored to its previous value,
|
||||
3. Beware that two temperatures are needed: internal temp and external temp and each should give temperature else the thermostat will be in ``security`` preset.
|
||||
|
||||
# Examples tuning
|
||||
|
||||
## Electrical heater
|
||||
- cycle: between 5 and 10 minutes,
|
||||
- minimal_activation_delay_sec: 30 seconds
|
||||
|
||||
## Central heating (gaz or fuel heating system)
|
||||
- cycle: between 30 and 60 min,
|
||||
- minimal_activation_delay_sec: 300 seconds (because of the response time)
|
||||
|
||||
## Temperature sensor will battery
|
||||
- security_delay_min: 60 min (because those sensors are leazy)
|
||||
|
||||
## Reponsive temperature sensor
|
||||
- security_delay_min: 15 min
|
||||
|
||||
## 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.
|
||||
``Eco``: 17
|
||||
``Comfort``: 19
|
||||
``Boost``: 20
|
||||
|
||||
When presence if off:
|
||||
``Eco``: 16.5
|
||||
``Comfort``: 17
|
||||
``Boost``: 18
|
||||
|
||||
Motion detector in my office is set to use ``Boost`` when motion is detected and ``Eco`` if not.
|
||||
|
||||
# Algorithm
|
||||
This integration uses a proportional algorithm. A Proportional algorithm is useful to avoid the oscillation around the target temperature. This algorithm is based on a cycle which alternate heating and stop heating. The proportion of heating vs not heating is determined by the difference between the temperature and the target temperature. Bigger the difference is and bigger is the proportion of heating inside the cycle.
|
||||
|
||||
This algorithm make the temperature converge and stop oscillating.
|
||||
|
||||
Depending of your area and heater, the convergente temperature can be under the targeted temperature. So a bias parameter is available to fix this. To find the right value of biais, just set it to 0 (no biais), let the temperature converge and see if it is near the targeted temperature. If not adjust the biais. A good value is 0.25 with my accumulator radiator (which are long to heat but keeps the heat for a long time).
|
||||
## TPI algorithm
|
||||
The TPI algorithm consist in the calculation at each cycle of a percentage of On state vs Off state for the heater using the target temperature, the current temperature in the room and the current external temperature.
|
||||
|
||||
A function parameter is available. Set it to "Linear" to have a linéar growth of temperature or set it to "Atan" to have a more aggressive curve to target temperature depending of your need.
|
||||
The percentage is calculated with this formula:
|
||||
|
||||
### Some results
|
||||
on_percent = coef_int * (target temperature - current temperature) + coef_ext * (target temperature - external temperature)
|
||||
Then make 0 <= on_percent <= 1
|
||||
|
||||
Convergence of temperature to target configured by preset:
|
||||

|
||||
Defaults values for coef_int and coef_ext are respectively: ``0.6`` and ``0.01``. Those defaults values are suitable for a standard well isolated room.
|
||||
|
||||
Cycle of on/off calculated by the integration:
|
||||

|
||||
To tune those coefficients keep in mind that:
|
||||
1. **if target temperature is not reach** after stable situation, you have to augment the ``coef_ext`` (the ``on_percent`` is too high),
|
||||
2. **if target temperature is exceeded** after stable situation, you have to decrease the ``coef_ext`` (the ``on_percent`` is too low),
|
||||
3. **if reaching the target temperature is too slow**, you can increase the ``coef_int`` to give more power to the heater,
|
||||
4. **if reaching the target temperature is too fast and some oscillations appears** around the target, you can decrease the ``coef_int`` to give less power to the heater
|
||||
|
||||
See some situations at [examples](#some-results).
|
||||
|
||||
# Services
|
||||
|
||||
This custom implementation offers some specific services to facilitate integration with others Home Assisstant components.
|
||||
|
||||
## Force the presence / occupancy
|
||||
This service allows you to force the presence status independantly of the presence sensor. This can be useful if you want to manage the presence through a service and not through a sensor. For example, you could use your alarm to force the absence when it is switched on.
|
||||
|
||||
The code to call this service is the following:
|
||||
```
|
||||
service: versatile_thermostat.set_presence
|
||||
data:
|
||||
presence: "off"
|
||||
target:
|
||||
entity_id: climate.my_thermostat
|
||||
```
|
||||
|
||||
## Change the temperature of presets
|
||||
This services is useful if you want to dynamically change the preset temperature. Instead of changing preset, some use-case need to change the temperature of the preset. So you can keep the Scheduler unchanged to manage the preset and adjust the temperature of the preset.
|
||||
If the changed preset is currently selectionned, the modification of the target temperature is immediate and will be taken into account at the next calculation cycle.
|
||||
|
||||
You can change the one or the both temperature (when present or when absent) of each preset.
|
||||
|
||||
Use the following code the set the temperature of the preset:
|
||||
```
|
||||
service: versatile_thermostat.set_preset_temperature
|
||||
data:
|
||||
preset: boost
|
||||
temperature: 17.8
|
||||
temperature_away: 15
|
||||
target:
|
||||
entity_id: climate.my_thermostat
|
||||
```
|
||||
|
||||
>  _*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.
|
||||
|
||||
# Custom attributes
|
||||
|
||||
To tune the algorithm you have access to all context seen and calculted by the thermostat through dedicated attributes. You can see (and use) those attributes in the "Development tools / states" HMI of HA. Enter your thermostat and you will see something like this:
|
||||

|
||||
|
||||
Custom attributes are the following:
|
||||
|
||||
| Attribute | Meaning |
|
||||
| ----------| --------|
|
||||
| ``hvac_modes`` | The list of modes supported by the thermostat |
|
||||
| ``min_temp`` | The minimal temperature |
|
||||
| ``max_temp`` | The maximal temperature |
|
||||
| ``preset_modes`` | The presets visible for this thermostat. Hidden presets are not showed here |
|
||||
| ``current_temperature`` | The current temperature as reported by the sensor |
|
||||
| ``temperature`` | The target temperature |
|
||||
| ``hvac_action`` | The action currently running by the heater. Can be idle, heating |
|
||||
| ``preset_mode`` | The currently selected preset. Can be one of the 'preset_modes' or a hidden preset like power |
|
||||
| ``[eco/comfort/boost]_temp`` | The temperature configured for the preset xxx |
|
||||
| ``[eco/comfort/boost]_away_temp`` | The temperature configured for the preset xxx when presence is off or not_home |
|
||||
| ``power_temp`` | The temperature used when shedding is detected |
|
||||
| ``on_percent`` | The percentage on calculated by the TPI algorithm |
|
||||
| ``on_time_sec`` | The On period in sec. Should be ```on_percent * cycle_min``` |
|
||||
| ``off_time_sec`` | The Off period in sec. Should be ```(1 - on_percent) * cycle_min``` |
|
||||
| ``cycle_min`` | The calculation cycle in minutes |
|
||||
| ``function`` | The algorithm used for cycle calculation |
|
||||
| ``tpi_coef_int`` | The ``coef_int`` of the TPI algorithm |
|
||||
| ``tpi_coef_ext`` | The ``coef_ext`` of the TPI algorithm |
|
||||
| ``saved_preset_mode`` | The last preset used before automatic switch of the preset |
|
||||
| ``saved_target_temp`` | The last temperature used before automatic switching |
|
||||
| ``window_state`` | The last known state of the window sensor. None if window is not configured |
|
||||
| ``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 |
|
||||
| ``last_temperature_datetime`` | The date and time in ISO8866 format of the last internal temperature reception |
|
||||
| ``last_ext_temperature_datetime`` | The date and time in ISO8866 format of the last external temperature reception |
|
||||
| ``security_state`` | The security state. true or false |
|
||||
| ``minimal_activation_delay_sec`` | The minimal activation delay in seconds |
|
||||
| ``last_update_datetime`` | The date and time in ISO8866 format of this state |
|
||||
| ``friendly_name`` | The name of the thermostat |
|
||||
| ``supported_features`` | A combination of all features supported by this thermostat. See official climate integration documentation for more informations |
|
||||
|
||||
# Some results
|
||||
|
||||
**Convergence of temperature to target configured by preset:**
|
||||

|
||||
|
||||
[Cycle of on/off calculated by the integration:](https://)
|
||||

|
||||
|
||||
**Coef_int too high (oscillations around the target)**
|
||||

|
||||
|
||||
**Algorithm calculation evolution**
|
||||

|
||||
See the code of this component [[below](#even-better-with-apex-chart-to-tune-your-thermostat)]
|
||||
|
||||
**Fine tuned thermostat**
|
||||
Thank's [impuR_Shozz](https://forum.hacf.fr/u/impur_shozz/summary) !
|
||||
We can see stability around the target temperature (consigne) and when at target the on_percent (puissance) is near 0.3 which seems a very good value.
|
||||
|
||||

|
||||
|
||||
Enjoy !
|
||||
|
||||
# Even better
|
||||
|
||||
## Even Better with Scheduler Component !
|
||||
|
||||
In order to enjoy the full power of Versatile Thermostat, I invite you to use it with https://github.com/nielsfaber/scheduler-component
|
||||
@@ -173,9 +405,9 @@ In this example I set ECO mode during the night and the day when nobody's at hom
|
||||
|
||||
I hope this example helps you, don't hesitate to give me your feedbacks !
|
||||
|
||||
## Even / even better with custom:simple-thermostat front integration
|
||||
The custom:simple-thermostat (see https://home.clouderial.fr/hacs/repository/158654878) is a great integration which allow some customisation which fits well with this thermostat.
|
||||
You can have something like that very easily 
|
||||
## Even-even better with custom:simple-thermostat front integration
|
||||
The ``custom:simple-thermostat`` [here](https://github.com/nervetattoo/simple-thermostat) is a great integration which allow some customisation which fits well with this thermostat.
|
||||
You can have something like that very easily 
|
||||
Example configuration:
|
||||
|
||||
```
|
||||
@@ -202,7 +434,46 @@ Example configuration:
|
||||
name: Porte sam
|
||||
```
|
||||
|
||||
## Contributions are welcome!
|
||||
## Even better with Apex-chart to tune your Thermostat
|
||||
You can get curve like presented in [some results](#some-results) with kind of Apex-chart configuration only using the custom attributes of the thermostat described [here](#custom-attributes):
|
||||
|
||||
```
|
||||
type: custom:apexcharts-card
|
||||
header:
|
||||
show: true
|
||||
title: Tuning chauffage
|
||||
show_states: true
|
||||
colorize_states: true
|
||||
update_interval: 60sec
|
||||
graph_span: 4h
|
||||
yaxis:
|
||||
- id: left
|
||||
show: true
|
||||
decimals: 2
|
||||
- id: right
|
||||
decimals: 2
|
||||
show: true
|
||||
opposite: true
|
||||
series:
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: temperature
|
||||
type: line
|
||||
name: Target temp
|
||||
curve: smooth
|
||||
yaxis_id: left
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: current_temperature
|
||||
name: Current temp
|
||||
curve: smooth
|
||||
yaxis_id: left
|
||||
- entity: climate.thermostat_mythermostat
|
||||
attribute: on_percent
|
||||
name: Power percent
|
||||
curve: stepline
|
||||
yaxis_id: right
|
||||
```
|
||||
|
||||
# Contributions are welcome!
|
||||
|
||||
If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
""" Implements the VersatileThermostat climate component """
|
||||
import math
|
||||
import logging
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from typing import Any, Mapping
|
||||
|
||||
# from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -11,7 +13,6 @@ from homeassistant.core import (
|
||||
callback,
|
||||
CoreState,
|
||||
DOMAIN as HA_DOMAIN,
|
||||
CALLBACK_TYPE,
|
||||
)
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
@@ -20,12 +21,14 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_state_change_event,
|
||||
async_track_time_interval,
|
||||
async_call_later,
|
||||
)
|
||||
|
||||
from homeassistant.exceptions import ConditionError
|
||||
from homeassistant.helpers import condition, entity_platform, config_validation as cv
|
||||
from homeassistant.helpers import (
|
||||
condition,
|
||||
entity_platform,
|
||||
) # , config_validation as cv
|
||||
|
||||
from homeassistant.components.climate.const import (
|
||||
ATTR_PRESET_MODE,
|
||||
@@ -38,7 +41,7 @@ from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_OFF,
|
||||
PRESET_ACTIVITY,
|
||||
PRESET_AWAY,
|
||||
# PRESET_AWAY,
|
||||
PRESET_BOOST,
|
||||
PRESET_COMFORT,
|
||||
PRESET_ECO,
|
||||
@@ -92,10 +95,16 @@ from .const import (
|
||||
CONF_PRESET_POWER,
|
||||
SUPPORT_FLAGS,
|
||||
PRESET_POWER,
|
||||
PRESET_SECURITY,
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
SERVICE_SET_PRESENCE,
|
||||
SERVICE_SET_PRESET_TEMPERATURE,
|
||||
PRESET_AWAY_SUFFIX,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
HIDDEN_PRESETS,
|
||||
)
|
||||
|
||||
from .prop_algorithm import PropAlgorithm
|
||||
@@ -115,71 +124,11 @@ async def async_setup_entry(
|
||||
|
||||
unique_id = entry.entry_id
|
||||
name = entry.data.get(CONF_NAME)
|
||||
heater_entity_id = entry.data.get(CONF_HEATER)
|
||||
cycle_min = entry.data.get(CONF_CYCLE_MIN)
|
||||
proportional_function = entry.data.get(CONF_PROP_FUNCTION)
|
||||
temp_sensor_entity_id = entry.data.get(CONF_TEMP_SENSOR)
|
||||
ext_temp_sensor_entity_id = entry.data.get(CONF_EXTERNAL_TEMP_SENSOR)
|
||||
power_sensor_entity_id = entry.data.get(CONF_POWER_SENSOR)
|
||||
max_power_sensor_entity_id = entry.data.get(CONF_MAX_POWER_SENSOR)
|
||||
window_sensor_entity_id = entry.data.get(CONF_WINDOW_SENSOR)
|
||||
window_delay_sec = entry.data.get(CONF_WINDOW_DELAY)
|
||||
motion_sensor_entity_id = entry.data.get(CONF_MOTION_SENSOR)
|
||||
motion_delay_sec = entry.data.get(CONF_MOTION_DELAY)
|
||||
motion_preset = entry.data.get(CONF_MOTION_PRESET)
|
||||
no_motion_preset = entry.data.get(CONF_NO_MOTION_PRESET)
|
||||
device_power = entry.data.get(CONF_DEVICE_POWER)
|
||||
tpi_coef_int = entry.data.get(CONF_TPI_COEF_INT)
|
||||
tpi_coef_ext = entry.data.get(CONF_TPI_COEF_EXT)
|
||||
presence_sensor_entity_id = entry.data.get(CONF_PRESENCE_SENSOR)
|
||||
power_temp = entry.data.get(CONF_PRESET_POWER)
|
||||
|
||||
presets = {}
|
||||
for (key, value) in CONF_PRESETS.items():
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry.data:
|
||||
presets[key] = entry.data.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
entity = VersatileThermostat(hass, unique_id, name, entry.data)
|
||||
|
||||
presets_away = {}
|
||||
for (key, value) in CONF_PRESETS_AWAY.items():
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry.data:
|
||||
presets_away[key] = entry.data.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
VersatileThermostat(
|
||||
hass,
|
||||
unique_id,
|
||||
name,
|
||||
heater_entity_id,
|
||||
cycle_min,
|
||||
proportional_function,
|
||||
temp_sensor_entity_id,
|
||||
ext_temp_sensor_entity_id,
|
||||
power_sensor_entity_id,
|
||||
max_power_sensor_entity_id,
|
||||
window_sensor_entity_id,
|
||||
window_delay_sec,
|
||||
motion_sensor_entity_id,
|
||||
motion_delay_sec,
|
||||
motion_preset,
|
||||
no_motion_preset,
|
||||
presets,
|
||||
presets_away,
|
||||
device_power,
|
||||
tpi_coef_int,
|
||||
tpi_coef_ext,
|
||||
presence_sensor_entity_id,
|
||||
power_temp,
|
||||
)
|
||||
],
|
||||
True,
|
||||
)
|
||||
async_add_entities([entity], True)
|
||||
VersatileThermostat.add_entity(entry.entry_id, entity)
|
||||
|
||||
# Add services
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
@@ -207,38 +156,10 @@ async def async_setup_entry(
|
||||
class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Representation of a Versatile Thermostat device."""
|
||||
|
||||
_name: str
|
||||
_heater_entity_id: str
|
||||
_prop_algorithm: PropAlgorithm
|
||||
_async_cancel_cycle: CALLBACK_TYPE
|
||||
_attr_preset_modes: list[str] | None
|
||||
# The list of VersatileThermostat entities
|
||||
_registry: dict[str, object] = {}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass,
|
||||
unique_id,
|
||||
name,
|
||||
heater_entity_id,
|
||||
cycle_min,
|
||||
proportional_function,
|
||||
temp_sensor_entity_id,
|
||||
ext_temp_sensor_entity_id,
|
||||
power_sensor_entity_id,
|
||||
max_power_sensor_entity_id,
|
||||
window_sensor_entity_id,
|
||||
window_delay_sec,
|
||||
motion_sensor_entity_id,
|
||||
motion_delay_sec,
|
||||
motion_preset,
|
||||
no_motion_preset,
|
||||
presets,
|
||||
presets_away,
|
||||
device_power,
|
||||
tpi_coef_int,
|
||||
tpi_coef_ext,
|
||||
presence_sensor_entity_id,
|
||||
power_temp,
|
||||
) -> None:
|
||||
def __init__(self, hass, unique_id, name, entry_infos) -> None:
|
||||
"""Initialize the thermostat."""
|
||||
|
||||
super().__init__()
|
||||
@@ -248,32 +169,100 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
self._unique_id = unique_id
|
||||
self._name = name
|
||||
self._heater_entity_id = heater_entity_id
|
||||
self._cycle_min = cycle_min
|
||||
self._proportional_function = proportional_function
|
||||
self._temp_sensor_entity_id = temp_sensor_entity_id
|
||||
self._ext_temp_sensor_entity_id = ext_temp_sensor_entity_id
|
||||
self._power_sensor_entity_id = power_sensor_entity_id
|
||||
self._max_power_sensor_entity_id = max_power_sensor_entity_id
|
||||
self._window_sensor_entity_id = window_sensor_entity_id
|
||||
self._window_delay_sec = window_delay_sec
|
||||
self._window_delay_sec = window_delay_sec
|
||||
self._motion_sensor_entity_id = motion_sensor_entity_id
|
||||
self._motion_delay_sec = motion_delay_sec
|
||||
self._motion_preset = motion_preset
|
||||
self._no_motion_preset = no_motion_preset
|
||||
self._prop_algorithm = None
|
||||
self._async_cancel_cycle = None
|
||||
self._hvac_mode = None
|
||||
self._target_temp = None
|
||||
self._saved_target_temp = None
|
||||
self._saved_preset_mode = None
|
||||
self._fan_mode = None
|
||||
self._humidity = None
|
||||
self._swing_mode = None
|
||||
self._current_power = None
|
||||
self._current_power_max = None
|
||||
self._window_state = None
|
||||
self._motion_state = None
|
||||
self._saved_hvac_mode = None
|
||||
self._window_call_cancel = None
|
||||
self._motion_call_cancel = None
|
||||
self._cur_ext_temp = None
|
||||
self._cur_temp = None
|
||||
self._ac_mode = None
|
||||
self._last_ext_temperature_mesure = None
|
||||
self._last_temperature_mesure = None
|
||||
self._cur_ext_temp = None
|
||||
self._presence_state = None
|
||||
self._overpowering_state = None
|
||||
self._should_relaunch_control_heating = None
|
||||
self._security_delay_min = None
|
||||
self._security_state = None
|
||||
|
||||
self.post_init(entry_infos)
|
||||
|
||||
def post_init(self, entry_infos):
|
||||
"""Finish the initialization of the thermostast"""
|
||||
|
||||
_LOGGER.info(
|
||||
"%s - Updating VersatileThermostat with infos %s",
|
||||
self,
|
||||
entry_infos,
|
||||
)
|
||||
# convert entry_infos into usable attributes
|
||||
presets = {}
|
||||
for (key, value) in CONF_PRESETS.items():
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry_infos:
|
||||
presets[key] = entry_infos.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
|
||||
presets_away = {}
|
||||
for (key, value) in CONF_PRESETS_AWAY.items():
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry_infos:
|
||||
presets_away[key] = entry_infos.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
|
||||
# Stop eventual cycle running
|
||||
if self._async_cancel_cycle is not None:
|
||||
self._async_cancel_cycle()
|
||||
self._async_cancel_cycle = None
|
||||
if self._window_call_cancel is not None:
|
||||
self._window_call_cancel()
|
||||
self._window_call_cancel = None
|
||||
if self._motion_call_cancel is not None:
|
||||
self._motion_call_cancel()
|
||||
self._motion_call_cancel = None
|
||||
|
||||
# Exploit usable attributs
|
||||
self._heater_entity_id = entry_infos.get(CONF_HEATER)
|
||||
self._cycle_min = entry_infos.get(CONF_CYCLE_MIN)
|
||||
self._proportional_function = entry_infos.get(CONF_PROP_FUNCTION)
|
||||
self._temp_sensor_entity_id = entry_infos.get(CONF_TEMP_SENSOR)
|
||||
self._ext_temp_sensor_entity_id = entry_infos.get(CONF_EXTERNAL_TEMP_SENSOR)
|
||||
self._attr_max_temp = entry_infos.get(CONF_TEMP_MAX)
|
||||
self._attr_min_temp = entry_infos.get(CONF_TEMP_MIN)
|
||||
self._power_sensor_entity_id = entry_infos.get(CONF_POWER_SENSOR)
|
||||
self._max_power_sensor_entity_id = entry_infos.get(CONF_MAX_POWER_SENSOR)
|
||||
self._window_sensor_entity_id = entry_infos.get(CONF_WINDOW_SENSOR)
|
||||
self._window_delay_sec = entry_infos.get(CONF_WINDOW_DELAY)
|
||||
self._motion_sensor_entity_id = entry_infos.get(CONF_MOTION_SENSOR)
|
||||
self._motion_delay_sec = entry_infos.get(CONF_MOTION_DELAY)
|
||||
self._motion_preset = entry_infos.get(CONF_MOTION_PRESET)
|
||||
self._no_motion_preset = entry_infos.get(CONF_NO_MOTION_PRESET)
|
||||
self._motion_on = (
|
||||
self._motion_sensor_entity_id is not None
|
||||
and self._motion_preset is not None
|
||||
and self._no_motion_preset is not None
|
||||
)
|
||||
|
||||
self._tpi_coef_int = tpi_coef_int
|
||||
self._tpi_coef_ext = tpi_coef_ext
|
||||
self._presence_sensor_entity_id = presence_sensor_entity_id
|
||||
self._power_temp = power_temp
|
||||
self._tpi_coef_int = entry_infos.get(CONF_TPI_COEF_INT)
|
||||
self._tpi_coef_ext = entry_infos.get(CONF_TPI_COEF_EXT)
|
||||
self._presence_sensor_entity_id = entry_infos.get(CONF_PRESENCE_SENSOR)
|
||||
self._power_temp = entry_infos.get(CONF_PRESET_POWER)
|
||||
|
||||
self._presence_on = self._presence_sensor_entity_id != None
|
||||
self._presence_on = self._presence_sensor_entity_id is not None
|
||||
|
||||
# TODO if self.ac_mode:
|
||||
# self.hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
|
||||
@@ -300,7 +289,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._saved_preset_mode = None
|
||||
|
||||
# Power management
|
||||
self._device_power = device_power
|
||||
self._device_power = entry_infos.get(CONF_DEVICE_POWER)
|
||||
self._pmax_on = False
|
||||
self._current_power = None
|
||||
self._current_power_max = None
|
||||
@@ -335,18 +324,23 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
)
|
||||
self._tpi_coef_ext = 0
|
||||
|
||||
self._security_delay_min = entry_infos.get(CONF_SECURITY_DELAY_MIN)
|
||||
self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY)
|
||||
self._last_temperature_mesure = datetime.now()
|
||||
self._last_ext_temperature_mesure = datetime.now()
|
||||
self._security_state = False
|
||||
self._saved_hvac_mode = None
|
||||
|
||||
# Initiate the ProportionalAlgorithm
|
||||
if self._prop_algorithm is not None:
|
||||
del self._prop_algorithm
|
||||
self._prop_algorithm = PropAlgorithm(
|
||||
self._proportional_function,
|
||||
self._tpi_coef_int,
|
||||
self._tpi_coef_ext,
|
||||
self._cycle_min,
|
||||
self._minimal_activation_delay,
|
||||
)
|
||||
|
||||
self._async_cancel_cycle = None
|
||||
self._window_call_cancel = None
|
||||
self._motion_call_cancel = None
|
||||
|
||||
self._should_relaunch_control_heating = False
|
||||
|
||||
# Memory synthesis state
|
||||
@@ -360,9 +354,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
if len(presets):
|
||||
self._support_flags = SUPPORT_FLAGS | SUPPORT_PRESET_MODE
|
||||
|
||||
for k, v in presets.items():
|
||||
if v != 0.0:
|
||||
self._attr_preset_modes.append(k)
|
||||
for key, val in presets.items():
|
||||
if val != 0.0:
|
||||
self._attr_preset_modes.append(key)
|
||||
|
||||
# self._attr_preset_modes = (
|
||||
# [PRESET_NONE] + list(presets.keys()) + [PRESET_ACTIVITY]
|
||||
@@ -380,7 +374,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"%s - Creation of a new VersatileThermostat entity: unique_id=%s heater_entity_id=%s",
|
||||
self,
|
||||
self.unique_id,
|
||||
heater_entity_id,
|
||||
self._heater_entity_id,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
@@ -455,14 +449,15 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
||||
if hvac_mode == HVAC_MODE_HEAT:
|
||||
self._hvac_mode = HVAC_MODE_HEAT
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
elif hvac_mode == HVAC_MODE_COOL:
|
||||
self._hvac_mode = HVAC_MODE_COOL
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
elif hvac_mode == HVAC_MODE_OFF:
|
||||
self._hvac_mode = HVAC_MODE_OFF
|
||||
if self._is_device_active:
|
||||
await self._async_heater_turn_off()
|
||||
await self._async_control_heating(force=True)
|
||||
else:
|
||||
_LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
|
||||
return
|
||||
@@ -472,14 +467,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
async def async_set_preset_mode(self, preset_mode):
|
||||
"""Set new preset mode."""
|
||||
await self._async_set_preset_mode_internal(preset_mode)
|
||||
await self._async_control_heating()
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
async def _async_set_preset_mode_internal(self, preset_mode, force=False):
|
||||
"""Set new preset mode."""
|
||||
_LOGGER.info("%s - Set preset_mode: %s force=%s", self, preset_mode, force)
|
||||
if (
|
||||
preset_mode not in (self._attr_preset_modes or [])
|
||||
and preset_mode != PRESET_POWER
|
||||
and preset_mode not in HIDDEN_PRESETS
|
||||
):
|
||||
raise ValueError(
|
||||
f"Got unsupported preset_mode {preset_mode}. Must be one of {self._attr_preset_modes}" # pylint: disable=line-too-long
|
||||
@@ -499,20 +494,24 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
if self._attr_preset_mode == PRESET_NONE:
|
||||
self._saved_target_temp = self._target_temp
|
||||
self._attr_preset_mode = preset_mode
|
||||
preset_temp = self.find_preset_temp(preset_mode)
|
||||
self._target_temp = (
|
||||
preset_temp if preset_mode != PRESET_POWER else self._power_temp
|
||||
)
|
||||
self._target_temp = self.find_preset_temp(preset_mode)
|
||||
|
||||
# Don't saved preset_mode if we are in POWER mode or in Away mode and presence detection is on
|
||||
if preset_mode != PRESET_POWER:
|
||||
self._saved_preset_mode = self._attr_preset_mode
|
||||
self.save_preset_mode()
|
||||
|
||||
self.recalculate()
|
||||
|
||||
def find_preset_temp(self, preset_mode):
|
||||
"""Find the right temperature of a preset considering the presence if configured"""
|
||||
if self._presence_on is False or self._presence_state in [STATE_ON, STATE_HOME]:
|
||||
if preset_mode == PRESET_SECURITY:
|
||||
return (
|
||||
self._target_temp
|
||||
) # in security just keep the current target temperature, the thermostat should be off
|
||||
if preset_mode == PRESET_POWER:
|
||||
return self._power_temp
|
||||
elif self._presence_on is False or self._presence_state in [
|
||||
STATE_ON,
|
||||
STATE_HOME,
|
||||
]:
|
||||
return self._presets[preset_mode]
|
||||
else:
|
||||
return self._presets_away[self.get_preset_away_name(preset_mode)]
|
||||
@@ -553,6 +552,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._target_temp = temperature
|
||||
self._attr_preset_mode = PRESET_NONE
|
||||
self.recalculate()
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
@callback
|
||||
async def entry_update_listener(
|
||||
@@ -638,21 +638,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
await self.async_startup()
|
||||
|
||||
# starts the cycle
|
||||
if self._cycle_min:
|
||||
self.async_on_remove(
|
||||
async_track_time_interval(
|
||||
self.hass,
|
||||
self._async_control_heating,
|
||||
interval=timedelta(minutes=self._cycle_min),
|
||||
)
|
||||
)
|
||||
# if self._cycle_min:
|
||||
# self.async_on_remove(
|
||||
# async_track_time_interval(
|
||||
# self.hass,
|
||||
# self._async_control_heating,
|
||||
# interval=timedelta(minutes=self._cycle_min),
|
||||
# )
|
||||
# )
|
||||
|
||||
async def async_startup(self):
|
||||
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
||||
_LOGGER.debug("%s - Calling async_startup", self)
|
||||
|
||||
@callback
|
||||
def _async_startup_internal(*_):
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
need_write_state = False
|
||||
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
|
||||
@@ -665,7 +665,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self,
|
||||
float(temperature_state.state),
|
||||
)
|
||||
self._async_update_temp(temperature_state)
|
||||
await self._async_update_temp(temperature_state)
|
||||
need_write_state = True
|
||||
|
||||
if self._ext_temp_sensor_entity_id:
|
||||
@@ -681,7 +681,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self,
|
||||
float(ext_temperature_state.state),
|
||||
)
|
||||
self._async_update_ext_temp(ext_temperature_state)
|
||||
await self._async_update_ext_temp(ext_temperature_state)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
|
||||
@@ -788,7 +788,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
await self.get_my_previous_state()
|
||||
|
||||
if self.hass.state == CoreState.running:
|
||||
_async_startup_internal()
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
@@ -820,7 +820,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
if old_state.attributes.get(ATTR_PRESET_MODE) in self._attr_preset_modes:
|
||||
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||
self._saved_preset_mode = self._attr_preset_mode
|
||||
self.save_preset_mode()
|
||||
|
||||
if not self._hvac_mode and old_state.state:
|
||||
self._hvac_mode = old_state.state
|
||||
@@ -867,8 +867,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
return
|
||||
|
||||
self._async_update_temp(new_state)
|
||||
await self._async_update_temp(new_state)
|
||||
self.recalculate()
|
||||
await self._async_control_heating(force=False)
|
||||
|
||||
async def _async_ext_temperature_changed(self, event):
|
||||
"""Handle external temperature changes."""
|
||||
@@ -881,8 +882,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
return
|
||||
|
||||
self._async_update_ext_temp(new_state)
|
||||
await self._async_update_ext_temp(new_state)
|
||||
self.recalculate()
|
||||
await self._async_control_heating(force=False)
|
||||
|
||||
@callback
|
||||
async def _async_windows_changed(self, event):
|
||||
@@ -992,6 +994,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
# We do not change the preset which is kept to ACTIVITY but only the target_temperature
|
||||
self._target_temp = self._presets[new_preset]
|
||||
self.recalculate()
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
if self._motion_call_cancel:
|
||||
self._motion_call_cancel()
|
||||
@@ -1023,24 +1026,35 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
def _async_update_temp(self, state):
|
||||
async def _async_update_temp(self, state):
|
||||
"""Update thermostat with latest state from sensor."""
|
||||
try:
|
||||
cur_temp = float(state.state)
|
||||
if math.isnan(cur_temp) or math.isinf(cur_temp):
|
||||
raise ValueError(f"Sensor has illegal state {state.state}")
|
||||
self._cur_temp = cur_temp
|
||||
self._last_temperature_mesure = datetime.now()
|
||||
# try to restart if we were in security mode
|
||||
if self._security_state:
|
||||
await self.async_set_hvac_mode(self._saved_hvac_mode)
|
||||
await self.restore_preset_mode()
|
||||
|
||||
except ValueError as ex:
|
||||
_LOGGER.error("Unable to update temperature from sensor: %s", ex)
|
||||
|
||||
@callback
|
||||
def _async_update_ext_temp(self, state):
|
||||
async def _async_update_ext_temp(self, state):
|
||||
"""Update thermostat with latest state from sensor."""
|
||||
try:
|
||||
cur_ext_temp = float(state.state)
|
||||
if math.isnan(cur_ext_temp) or math.isinf(cur_ext_temp):
|
||||
raise ValueError(f"Sensor has illegal state {state.state}")
|
||||
self._cur_ext_temp = cur_ext_temp
|
||||
self._last_ext_temperature_mesure = datetime.now()
|
||||
# try to restart if we were in security mode
|
||||
if self._security_state:
|
||||
await self.async_set_hvac_mode(self._saved_hvac_mode)
|
||||
await self.restore_preset_mode()
|
||||
except ValueError as ex:
|
||||
_LOGGER.error("Unable to update external temperature from sensor: %s", ex)
|
||||
|
||||
@@ -1064,6 +1078,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
raise ValueError(f"Sensor has illegal state {new_state.state}")
|
||||
self._current_power = current_power
|
||||
|
||||
if self._attr_preset_mode == PRESET_POWER:
|
||||
await self._async_control_heating()
|
||||
|
||||
except ValueError as ex:
|
||||
_LOGGER.error("Unable to update current_power from sensor: %s", ex)
|
||||
|
||||
@@ -1086,6 +1103,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
if math.isnan(current_power_max) or math.isinf(current_power_max):
|
||||
raise ValueError(f"Sensor has illegal state {new_state.state}")
|
||||
self._current_power_max = current_power_max
|
||||
if self._attr_preset_mode == PRESET_POWER:
|
||||
await self._async_control_heating()
|
||||
|
||||
except ValueError as ex:
|
||||
_LOGGER.error("Unable to update current_power from sensor: %s", ex)
|
||||
@@ -1105,13 +1124,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
return
|
||||
|
||||
self._update_presence(new_state.state)
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
def _update_presence(self, new_state):
|
||||
_LOGGER.debug("%s - Updating presence. New state is %s", self, new_state)
|
||||
self._presence_state = new_state
|
||||
if self._attr_preset_mode == PRESET_POWER or self._presence_on is False:
|
||||
if self._attr_preset_mode in HIDDEN_PRESETS or self._presence_on is False:
|
||||
_LOGGER.info(
|
||||
"%s - Ignoring presence change cause in Power preset or presence not configured",
|
||||
"%s - Ignoring presence change cause in Power or Security preset or presence not configured",
|
||||
self,
|
||||
)
|
||||
return
|
||||
@@ -1192,6 +1212,26 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
HA_DOMAIN, SERVICE_TURN_OFF, data, context=self._context
|
||||
)
|
||||
|
||||
def save_preset_mode(self):
|
||||
"""Save the current preset mode to be restored later
|
||||
We never save a hidden preset mode
|
||||
"""
|
||||
if (
|
||||
self._attr_preset_mode not in HIDDEN_PRESETS
|
||||
and self._attr_preset_mode is not None
|
||||
):
|
||||
self._saved_preset_mode = self._attr_preset_mode
|
||||
|
||||
async def restore_preset_mode(self):
|
||||
"""Restore a previous preset mode
|
||||
We never restore a hidden preset mode. Normally that is not possible
|
||||
"""
|
||||
if (
|
||||
self._saved_preset_mode not in HIDDEN_PRESETS
|
||||
and self._saved_preset_mode is not None
|
||||
):
|
||||
await self._async_set_preset_mode_internal(self._saved_preset_mode)
|
||||
|
||||
async def check_overpowering(self) -> bool:
|
||||
"""Check the overpowering condition
|
||||
Turn the preset_mode of the heater to 'power' if power conditions are exceeded
|
||||
@@ -1225,11 +1265,31 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self,
|
||||
self._saved_preset_mode,
|
||||
)
|
||||
await self.async_set_preset_mode(
|
||||
self._saved_preset_mode if self._saved_preset_mode else PRESET_NONE
|
||||
)
|
||||
await self.restore_preset_mode()
|
||||
|
||||
async def _async_control_heating(self, time=None):
|
||||
def check_date_temperature(self) -> bool:
|
||||
"""Check if last temperature date is too long"""
|
||||
now = datetime.now()
|
||||
delta_temp = (now - self._last_temperature_mesure).total_seconds() / 60.0
|
||||
delta_ext_temp = (
|
||||
now - self._last_ext_temperature_mesure
|
||||
).total_seconds() / 60.0
|
||||
if (
|
||||
delta_temp > self._security_delay_min
|
||||
or delta_ext_temp > self._security_delay_min
|
||||
):
|
||||
_LOGGER.warning(
|
||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f)",
|
||||
self,
|
||||
self._security_delay_min,
|
||||
delta_temp,
|
||||
delta_ext_temp,
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def _async_control_heating(self, force=False, time=None):
|
||||
"""The main function used to run the calculation at each cycle"""
|
||||
|
||||
overpowering: bool = await self.check_overpowering()
|
||||
@@ -1240,65 +1300,105 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
self._attr_preset_mode,
|
||||
)
|
||||
await self._async_heater_turn_off()
|
||||
_LOGGER.debug("%s - End of cycle (0)", self)
|
||||
return
|
||||
|
||||
on_time_sec: int = self._prop_algorithm.on_time_sec
|
||||
off_time_sec: int = self._prop_algorithm.off_time_sec
|
||||
_LOGGER.info(
|
||||
"%s - Running new cycle at %s. on_time_sec=%.0f, off_time_sec=%.0f",
|
||||
"%s - Checking new cycle. on_time_sec=%.0f, off_time_sec=%.0f, security_state=%s, preset_mode=%s",
|
||||
self,
|
||||
time,
|
||||
on_time_sec,
|
||||
off_time_sec,
|
||||
self._security_state,
|
||||
self._attr_preset_mode,
|
||||
)
|
||||
|
||||
# Cancel eventual previous cycle if any
|
||||
if self._async_cancel_cycle is not None:
|
||||
_LOGGER.debug(
|
||||
"%s - A previous cycle is alredy running -> waits for its end", self
|
||||
)
|
||||
self._should_relaunch_control_heating = True
|
||||
return
|
||||
# await self._async_cancel_cycle()
|
||||
# self._async_cancel_cycle = None
|
||||
# Don't turn off if we will turn on just after
|
||||
# if on_time_sec <= 0:
|
||||
# await self._async_heater_turn_off()
|
||||
|
||||
if self._hvac_mode == HVAC_MODE_HEAT and on_time_sec > 0:
|
||||
_LOGGER.info(
|
||||
"%s - start heating for %d min %d sec ",
|
||||
self,
|
||||
on_time_sec // 60,
|
||||
on_time_sec % 60,
|
||||
)
|
||||
|
||||
await self._async_heater_turn_on()
|
||||
|
||||
async def _turn_off(_):
|
||||
if force:
|
||||
_LOGGER.debug("%s - we force a new cycle", self)
|
||||
self._async_cancel_cycle()
|
||||
self._async_cancel_cycle = None
|
||||
if self._should_relaunch_control_heating:
|
||||
_LOGGER.debug("Don't stop cause a cycle have to be relaunch")
|
||||
self._should_relaunch_control_heating = False
|
||||
await self._async_control_heating()
|
||||
return
|
||||
else:
|
||||
_LOGGER.info(
|
||||
"%s - stop heating for %d min %d sec",
|
||||
self,
|
||||
off_time_sec // 60,
|
||||
off_time_sec % 60,
|
||||
)
|
||||
await self._async_heater_turn_off()
|
||||
self.update_custom_attributes()
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s - A previous cycle is alredy running and no force -> waits for its end",
|
||||
self,
|
||||
)
|
||||
self._should_relaunch_control_heating = True
|
||||
_LOGGER.debug("%s - End of cycle (1)", self)
|
||||
return
|
||||
|
||||
# Program turn off
|
||||
self._async_cancel_cycle = async_call_later(
|
||||
self.hass,
|
||||
on_time_sec,
|
||||
_turn_off,
|
||||
)
|
||||
if self._hvac_mode == HVAC_MODE_HEAT and on_time_sec > 0:
|
||||
|
||||
async def _turn_on_off_later(
|
||||
on: bool, time, heater_action, next_cycle_action
|
||||
):
|
||||
if self._async_cancel_cycle:
|
||||
self._async_cancel_cycle()
|
||||
self._async_cancel_cycle = None
|
||||
_LOGGER.debug("%s - Stopping cycle during calculation", self)
|
||||
|
||||
check_dates = self.check_date_temperature()
|
||||
if time > 0 and on is True and check_dates is False:
|
||||
_LOGGER.warning("%s - Set the thermostat into security mode", self)
|
||||
self._security_state = True
|
||||
self._saved_hvac_mode = self.hvac_mode
|
||||
self.save_preset_mode()
|
||||
await self._async_set_preset_mode_internal(PRESET_SECURITY)
|
||||
await self.async_set_hvac_mode(HVAC_MODE_OFF)
|
||||
# The cycle is not restarted in security mode. It will be restarted by a condition changes
|
||||
_LOGGER.debug("%s - End of cycle (2)", self)
|
||||
return
|
||||
if check_dates:
|
||||
self._security_state = False
|
||||
|
||||
action_label = "start" if on else "stop"
|
||||
if self._should_relaunch_control_heating:
|
||||
_LOGGER.debug(
|
||||
"Don't %s cause a cycle have to be relaunch", action_label
|
||||
)
|
||||
self._should_relaunch_control_heating = False
|
||||
self.hass.create_task(self._async_control_heating())
|
||||
# await self._async_control_heating()
|
||||
_LOGGER.debug("%s - End of cycle (3)", self)
|
||||
return
|
||||
|
||||
if time > 0:
|
||||
_LOGGER.info(
|
||||
"%s - !!! %s heating for %d min %d sec",
|
||||
self,
|
||||
action_label,
|
||||
time // 60,
|
||||
time % 60,
|
||||
)
|
||||
await heater_action()
|
||||
else:
|
||||
_LOGGER.debug("%s - No action on heater cause duration is 0", self)
|
||||
self.update_custom_attributes()
|
||||
self._async_cancel_cycle = async_call_later(
|
||||
self.hass,
|
||||
time,
|
||||
next_cycle_action,
|
||||
)
|
||||
|
||||
async def _turn_on_later(_):
|
||||
await _turn_on_off_later(
|
||||
on=True,
|
||||
time=self._prop_algorithm.on_time_sec,
|
||||
heater_action=self._async_heater_turn_on,
|
||||
next_cycle_action=_turn_off_later,
|
||||
)
|
||||
|
||||
async def _turn_off_later(_):
|
||||
await _turn_on_off_later(
|
||||
on=False,
|
||||
time=self._prop_algorithm.off_time_sec,
|
||||
heater_action=self._async_heater_turn_off,
|
||||
next_cycle_action=_turn_on_later,
|
||||
)
|
||||
|
||||
await _turn_on_later(None)
|
||||
|
||||
elif self._is_device_active:
|
||||
_LOGGER.info(
|
||||
@@ -1352,10 +1452,16 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"tpi_coef_ext": self._tpi_coef_ext,
|
||||
"saved_preset_mode": self._saved_preset_mode,
|
||||
"saved_target_temp": self._saved_target_temp,
|
||||
"saved_hvac_mode": self._saved_hvac_mode,
|
||||
"window_state": self._window_state,
|
||||
"motion_state": self._motion_state,
|
||||
"overpowering_state": self._overpowering_state,
|
||||
"presence_state": self._presence_state,
|
||||
"security_delay_min": self._security_delay_min,
|
||||
"last_temperature_datetime": self._last_temperature_mesure.isoformat(),
|
||||
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.isoformat(),
|
||||
"security_state": self._security_state,
|
||||
"minimal_activation_delay_sec": self._minimal_activation_delay,
|
||||
"last_update_datetime": datetime.now().isoformat(),
|
||||
}
|
||||
self.async_write_ha_state()
|
||||
@@ -1382,6 +1488,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
"""
|
||||
_LOGGER.info("%s - Calling service_set_presence, presence: %s", self, presence)
|
||||
self._update_presence(presence)
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
async def service_set_preset_temperature(
|
||||
self, preset, temperature=None, temperature_away=None
|
||||
@@ -1417,3 +1524,27 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
||||
# If the changed preset is active, change the current temperature
|
||||
if self._attr_preset_mode == preset:
|
||||
await self._async_set_preset_mode_internal(preset, force=True)
|
||||
await self._async_control_heating(force=True)
|
||||
|
||||
@classmethod
|
||||
def add_entity(cls, entry_id, entity):
|
||||
"""Adds an entity into the VersatileRegistry entities"""
|
||||
_LOGGER.debug("Adding entity %s", entry_id)
|
||||
cls._registry[entry_id] = entity
|
||||
_LOGGER.debug("Entity registry is now %s", cls._registry)
|
||||
|
||||
@classmethod
|
||||
async def update_entity(cls, entry_id, infos):
|
||||
"""Updates an existing entity referenced by entry_id with the infos in arguments"""
|
||||
entity: VersatileThermostat = cls._registry.get(entry_id)
|
||||
if entity is None:
|
||||
_LOGGER.warning(
|
||||
"Tries to update VersatileThermostat entity %s but was not found in thermostat registry",
|
||||
entry_id,
|
||||
)
|
||||
return
|
||||
|
||||
_LOGGER.debug("We have found the entity to update")
|
||||
entity.post_init(infos)
|
||||
|
||||
await entity.async_added_to_hass()
|
||||
|
||||
@@ -3,9 +3,9 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import copy
|
||||
from collections.abc import Mapping
|
||||
import voluptuous as vol
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import callback
|
||||
@@ -14,6 +14,8 @@ from homeassistant.config_entries import (
|
||||
ConfigFlow as HAConfigFlow,
|
||||
OptionsFlow,
|
||||
)
|
||||
|
||||
# import homeassistant.helpers.entity_registry as entity_registry
|
||||
from homeassistant.data_entry_flow import FlowHandler
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
@@ -45,8 +47,14 @@ from .const import (
|
||||
CONF_TPI_COEF_INT,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
)
|
||||
|
||||
from .climate import VersatileThermostat
|
||||
|
||||
# from .climate import VersatileThermostat
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -117,6 +125,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
),
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -174,6 +184,15 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_ADVANCED_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_MINIMAL_ACTIVATION_DELAY, default=10
|
||||
): cv.positive_int,
|
||||
vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
async def validate_input(self, data: dict) -> dict[str]:
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
@@ -303,6 +322,17 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"presence",
|
||||
self.STEP_PRESENCE_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_step_advanced,
|
||||
)
|
||||
|
||||
async def async_step_advanced(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the advanced parameter flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_advanced user_input=%s", user_input)
|
||||
|
||||
return await self.generic_step(
|
||||
"advanced",
|
||||
self.STEP_ADVANCED_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_finalize, # pylint: disable=no-member
|
||||
)
|
||||
|
||||
@@ -417,7 +447,7 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"power",
|
||||
self.STEP_POWER_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_step_presence, # pylint: disable=no-member
|
||||
self.async_step_presence,
|
||||
)
|
||||
|
||||
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -430,13 +460,52 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"presence",
|
||||
self.STEP_PRESENCE_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_finalize, # pylint: disable=no-member
|
||||
self.async_step_advanced,
|
||||
)
|
||||
|
||||
async def async_finalize(self):
|
||||
async def async_step_advanced(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the advanced flow steps"""
|
||||
_LOGGER.debug(
|
||||
"Into OptionsFlowHandler.async_step_advanced user_input=%s", user_input
|
||||
)
|
||||
|
||||
return await self.generic_step(
|
||||
"advanced",
|
||||
self.STEP_ADVANCED_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_end,
|
||||
)
|
||||
|
||||
async def async_end(self):
|
||||
"""Finalization of the ConfigEntry creation"""
|
||||
_LOGGER.debug(
|
||||
"CTOR ConfigFlow.async_finalize - updating entry with: %s", self._infos
|
||||
)
|
||||
# Find eventual existing entity to update it
|
||||
# removing entities from registry (they will be recreated)
|
||||
# ent_reg = entity_registry.async_get(self.hass)
|
||||
|
||||
# reg_entities = {
|
||||
# ent.unique_id: ent.entity_id
|
||||
# for ent in entity_registry.async_entries_for_config_entry(
|
||||
# ent_reg, self.config_entry.entry_id
|
||||
# )
|
||||
# }
|
||||
#
|
||||
# for entry in entity_registry.async_entries_for_config_entry(
|
||||
# ent_reg, self.config_entry.entry_id
|
||||
# ):
|
||||
# entity: VersatileThermostat = ent_reg.async_get(entry.entity_id)
|
||||
# entity.async_registry_entry_updated(self._infos)
|
||||
|
||||
_LOGGER.debug(
|
||||
"We have found entities to update: %s", self.config_entry.entry_id
|
||||
)
|
||||
await VersatileThermostat.update_entity(self.config_entry.entry_id, self._infos)
|
||||
|
||||
# for entity_id in reg_entities.values():
|
||||
# _LOGGER.info("Recreating entity %s due to configuration change", entity_id)
|
||||
# ent_reg.async_remove(entity_id)
|
||||
#
|
||||
self.hass.config_entries.async_update_entry(self.config_entry, data=self._infos)
|
||||
return self.async_create_entry(title=None, data=None)
|
||||
|
||||
@@ -14,6 +14,9 @@ from .prop_algorithm import (
|
||||
)
|
||||
|
||||
PRESET_POWER = "power"
|
||||
PRESET_SECURITY = "security"
|
||||
|
||||
HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
|
||||
|
||||
DOMAIN = "versatile_thermostat"
|
||||
|
||||
@@ -35,6 +38,10 @@ CONF_TPI_COEF_INT = "tpi_coef_int"
|
||||
CONF_TPI_COEF_EXT = "tpi_coef_ext"
|
||||
CONF_PRESENCE_SENSOR = "presence_sensor_entity_id"
|
||||
CONF_PRESET_POWER = "power_temp"
|
||||
CONF_MINIMAL_ACTIVATION_DELAY = "minimal_activation_delay"
|
||||
CONF_TEMP_MIN = "temp_min"
|
||||
CONF_TEMP_MAX = "temp_max"
|
||||
CONF_SECURITY_DELAY_MIN = "security_delay_min"
|
||||
|
||||
CONF_PRESETS = {
|
||||
p: f"{p}_temp"
|
||||
@@ -81,6 +88,10 @@ ALL_CONF = (
|
||||
CONF_TPI_COEF_INT,
|
||||
CONF_TPI_COEF_EXT,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_TEMP_MIN,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
]
|
||||
+ CONF_PRESETS_VALUES
|
||||
+ CONF_PRESETS_AWAY_VALUES,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import math
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,20 +20,22 @@ class PropAlgorithm:
|
||||
tpi_coef_int,
|
||||
tpi_coef_ext,
|
||||
cycle_min: int,
|
||||
minimal_activation_delay: int,
|
||||
):
|
||||
"""Initialisation of the Proportional Algorithm"""
|
||||
_LOGGER.debug(
|
||||
"Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d",
|
||||
"Creation new PropAlgorithm function_type: %s, tpi_coef_int: %s, tpi_coef_ext: %s, cycle_min:%d, minimal_activation_delay:%d",
|
||||
function_type,
|
||||
tpi_coef_int,
|
||||
tpi_coef_ext,
|
||||
cycle_min,
|
||||
minimal_activation_delay,
|
||||
)
|
||||
# TODO test function_type, bias, cycle_min
|
||||
self._function = function_type
|
||||
self._tpi_coef_int = tpi_coef_int
|
||||
self._tpi_coef_ext = tpi_coef_ext
|
||||
self._cycle_min = cycle_min
|
||||
self._minimal_activation_delay = minimal_activation_delay
|
||||
self._on_percent = 0
|
||||
self._on_time_sec = 0
|
||||
self._off_time_sec = self._cycle_min * 60
|
||||
@@ -74,12 +75,19 @@ class PropAlgorithm:
|
||||
self._on_time_sec = self._on_percent * self._cycle_min * 60
|
||||
|
||||
# Do not heat for less than xx sec
|
||||
if self._on_time_sec < PROPORTIONAL_MIN_DURATION_SEC:
|
||||
_LOGGER.debug(
|
||||
"No heating period due to heating period too small (%f < %f)",
|
||||
self._on_time_sec,
|
||||
PROPORTIONAL_MIN_DURATION_SEC,
|
||||
)
|
||||
if self._on_time_sec < self._minimal_activation_delay:
|
||||
if self._on_time_sec > 0:
|
||||
_LOGGER.info(
|
||||
"No heating period due to heating period too small (%f < %f)",
|
||||
self._on_time_sec,
|
||||
self._minimal_activation_delay,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"No heating period due to heating period too small (%f < %f)",
|
||||
self._on_time_sec,
|
||||
self._minimal_activation_delay,
|
||||
)
|
||||
self._on_time_sec = 0
|
||||
|
||||
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
||||
|
||||
@@ -3,7 +3,6 @@ set_presence:
|
||||
description: Force the presence mode in thermostat
|
||||
target:
|
||||
entity:
|
||||
multiple: true
|
||||
integration: versatile_thermostat
|
||||
fields:
|
||||
presence:
|
||||
@@ -26,7 +25,6 @@ set_preset_temperature:
|
||||
description: Change the target temperature of a preset
|
||||
target:
|
||||
entity:
|
||||
multiple: true
|
||||
integration: versatile_thermostat
|
||||
fields:
|
||||
preset:
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)"
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -69,6 +71,14 @@
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -91,7 +101,9 @@
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)"
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -148,6 +160,14 @@
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)"
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -69,6 +71,14 @@
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -91,7 +101,9 @@
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)"
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -148,6 +160,14 @@
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced parameters",
|
||||
"description": "Configuration of advanced parameters. Leave the default values if you don't know what you are doing.\nThis parameters can lead to a very bad temperature or power regulation.",
|
||||
"data": {
|
||||
"minimal_activation_delay": "Delay in secondes under which the equipment will not be activated",
|
||||
"security_delay_min": "Maximum allowed delay in minutes between two temperature mesures. Above this delay, the thermostat will turn to a sceurity off state"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)"
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -69,6 +71,14 @@
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'absence"
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"data": {
|
||||
"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 éteinte de sécurité"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -91,7 +101,9 @@
|
||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||
"cycle_min": "Durée du cycle (minutes)",
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)"
|
||||
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -148,6 +160,14 @@
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'absence"
|
||||
}
|
||||
},
|
||||
"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.",
|
||||
"data": {
|
||||
"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 éteinte de sécurité"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
||||
BIN
images/config-advanced.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
images/config-main.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 43 KiB |
BIN
images/config-power.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
images/config-presence.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
images/config-presets.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
images/config-tpi.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
images/dev-tools-climate.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
BIN
images/results-3.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
images/results-4.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
images/results-fine-tuned.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
images/tips.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |