Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
717c893c75 | ||
|
|
5b8fb81053 | ||
|
|
8b77d9d75f | ||
|
|
3d977c4981 | ||
|
|
115df52f67 | ||
|
|
3371197594 | ||
|
|
7e63c9aa79 | ||
|
|
100cfaeac9 | ||
|
|
77631e94d9 | ||
|
|
49c85eeb2b | ||
|
|
bb2c1d328b | ||
|
|
b607fb1075 | ||
|
|
4786949aeb |
6
.bashrc
Normal file
6
.bashrc
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
echo "Sourcing .bashrc"
|
||||
alias ll='ls -l'
|
||||
export HA='/home/vscode/core'
|
||||
cd $HA
|
||||
source venv/bin/activate
|
||||
@@ -7,6 +7,9 @@ logger:
|
||||
|
||||
# If you need to debug uncommment the line below (doc: https://www.home-assistant.io/integrations/debugpy/)
|
||||
debugpy:
|
||||
start: true
|
||||
wait: false
|
||||
port: 5678
|
||||
|
||||
input_number:
|
||||
fake_temperature_sensor1:
|
||||
@@ -15,25 +18,28 @@ input_number:
|
||||
max: 35
|
||||
step: .1
|
||||
icon: mdi:thermometer
|
||||
unit_of_measurement: °C
|
||||
fake_external_temperature_sensor1:
|
||||
name: Ext Temperature
|
||||
min: -10
|
||||
max: 35
|
||||
step: .1
|
||||
icon: mdi:home-thermometer
|
||||
unit_of_measurement: °C
|
||||
fake_current_power:
|
||||
name: Current power
|
||||
min: 0
|
||||
max: 1000
|
||||
step: 10
|
||||
icon: mdi:flash
|
||||
unit_of_measurement: kW
|
||||
fake_current_power_max:
|
||||
name: Current power max threshold
|
||||
min: 0
|
||||
max: 1000
|
||||
step: 10
|
||||
icon: mdi:flash
|
||||
|
||||
unit_of_measurement: kW
|
||||
|
||||
input_boolean:
|
||||
# input_boolean to simulate the windows entity. Only for development environment.
|
||||
@@ -58,3 +64,77 @@ input_boolean:
|
||||
fake_presence_sensor1:
|
||||
name: Presence Sensor 1
|
||||
icon: mdi:home
|
||||
|
||||
climate:
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat1
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat2
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat3
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat4
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat5
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat6
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat7
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat8
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
- platform: generic_thermostat
|
||||
name: Underlying thermostat9
|
||||
heater: input_boolean.fake_heater_switch3
|
||||
target_sensor: input_number.fake_temperature_sensor1
|
||||
|
||||
recorder:
|
||||
include:
|
||||
domains:
|
||||
- input_boolean
|
||||
- input_number
|
||||
- switch
|
||||
- climate
|
||||
- sensor
|
||||
|
||||
template:
|
||||
- binary_sensor:
|
||||
- name: maison_occupee
|
||||
unique_id: maison_occupee
|
||||
state: "{{is_state('person.jmc', 'home') }}"
|
||||
device_class: occupancy
|
||||
|
||||
switch:
|
||||
- platform: template
|
||||
switches:
|
||||
pilote_sdb_rdc:
|
||||
friendly_name: "Pilote chauffage SDB RDC"
|
||||
value_template: "{{ is_state_attr('switch_seche_serviettes_sdb_rdc', 'sensor_state', 'on') }}"
|
||||
turn_on:
|
||||
service: select.select_option
|
||||
data:
|
||||
option: comfort
|
||||
target:
|
||||
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
||||
|
||||
turn_off:
|
||||
service: select.select_option
|
||||
data:
|
||||
option: comfort-2
|
||||
target:
|
||||
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
|
||||
// "image": "ghcr.io/ludeeus/devcontainer/integration:latest",
|
||||
{
|
||||
"image": "ghcr.io/ludeeus/devcontainer/integration:latest",
|
||||
"image": "mcr.microsoft.com/vscode/devcontainers/python:0-3.10",
|
||||
"name": "Versatile Thermostat integration",
|
||||
"context": "..",
|
||||
"appPort": [
|
||||
"9123:8123"
|
||||
],
|
||||
"postCreateCommand": "container install",
|
||||
// "postCreateCommand": "container install",
|
||||
"postCreateCommand": "./container install",
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"github.vscode-pull-request-github",
|
||||
"ryanluker.vscode-coverage-gutters",
|
||||
"ms-python.vscode-pylance"
|
||||
],
|
||||
"mounts": [
|
||||
"source=/Users/jmcollin/SugarSync/Projets/home-assistant/core,target=/home/vscode/core,type=bind,consistency=cached",
|
||||
"source=${localWorkspaceFolder}/.devcontainer/configuration.yaml,target=/home/vscode/core/config/configuration.yaml,type=bind,consistency=cached",
|
||||
"source=${localWorkspaceFolder}/custom_components,target=/home/vscode/core/config/custom_components,type=bind,consistency=cached"
|
||||
],
|
||||
"settings": {
|
||||
"files.eol": "\n",
|
||||
"editor.tabSize": 4,
|
||||
@@ -25,7 +32,7 @@
|
||||
"terminal.integrated.defaultProfile.linux": "Bash Profile",
|
||||
// "terminal.integrated.shell.linux": "/bin/bash",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.analysis.autoSearchPaths": false,
|
||||
"python.analysis.autoSearchPaths": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
|
||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -11,9 +11,13 @@
|
||||
"host": "localhost",
|
||||
"justMyCode": false,
|
||||
"pathMappings": [
|
||||
// {
|
||||
// "localRoot": "${workspaceFolder}",
|
||||
// "remoteRoot": "."
|
||||
//},
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "."
|
||||
"localRoot": "${workspaceFolder}/../core",
|
||||
"remoteRoot": "/home/vscode/core"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -4,5 +4,9 @@
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"files.associations": {
|
||||
"*.yaml": "home-assistant"
|
||||
}
|
||||
},
|
||||
"python.analysis.extraPaths": [
|
||||
"/home/vscode/core",
|
||||
"/workspaces/versatile_thermostat"
|
||||
]
|
||||
}
|
||||
26
.vscode/tasks.json
vendored
26
.vscode/tasks.json
vendored
@@ -4,25 +4,43 @@
|
||||
{
|
||||
"label": "Run Home Assistant on port 9123",
|
||||
"type": "shell",
|
||||
"command": "container start",
|
||||
"command": "./container start",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Restart Home Assistant on port 9123",
|
||||
"type": "shell",
|
||||
"command": "./container restart",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Home Assistant translations update",
|
||||
"type": "shell",
|
||||
"command": "./container translations",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Home Assistant hassfest",
|
||||
"type": "shell",
|
||||
"command": "./container hassfest",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run Home Assistant configuration against /config",
|
||||
"type": "shell",
|
||||
"command": "container check-config",
|
||||
"command": "./container check-config",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Upgrade Home Assistant to latest dev",
|
||||
"type": "shell",
|
||||
"command": "container install",
|
||||
"command": "./container install",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Install a specific version of Home Assistant",
|
||||
"type": "shell",
|
||||
"command": "container set-version",
|
||||
"command": "./container set-version",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
|
||||
65
README-fr.md
65
README-fr.md
@@ -13,12 +13,13 @@
|
||||
- [HACS installation (recommendé)](#hacs-installation-recommendé)
|
||||
- [Installation manuelle](#installation-manuelle)
|
||||
- [Configuration](#configuration)
|
||||
- [Configuration minimale](#configuration-minimale)
|
||||
- [Choix des attributs de base](#choix-des-attributs-de-base)
|
||||
- [Sélectionnez l'entité pilotée](#sélectionnez-lentité-pilotée)
|
||||
- [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 gestion de la puissance](#configurer-la-gestion-de-la-puissance)
|
||||
- [Configurer la présence ou l'occupation](#configurer-la-présence-ou-loccupation)
|
||||
- [Configuration avancée](#configuration-avancée)
|
||||
- [Exemples de réglage](#exemples-de-réglage)
|
||||
@@ -40,16 +41,22 @@
|
||||
- [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)
|
||||
Ce thermostat peut piloter 2 types d'équipement:
|
||||
1. un radiateur qui ne fonctionne qu'en mode marche/arrêt (nommé ```thermostat_over_switch```). La configuration minimale nécessaire pour utiliser ce type thermostat est :
|
||||
a. un équipement comme un radiateur (un ```switch``` ou équivalent),
|
||||
b. une sonde de température pour la pièce (ou un input_number),
|
||||
c. un capteur de température externe (pensez à l'intégration météo si vous n'en avez pas)
|
||||
2. un autre thermostat qui a ses propres modes de fonctionnement (nommé ```thermostat_over_climate```). Pour ce type de thermostat la configuration minimale nécessite :
|
||||
a. un équipement comme une climatisation qui est pilotée par sa propre entity de type ```climate```,
|
||||
b. une sonde de température pour la pièce (ou un input_number),
|
||||
c. un capteur de température externe (pensez à l'intégration météo si vous n'en avez pas)
|
||||
|
||||
Le type ```thermostat_over_climate``` permet d'ajouter à votre équipement existant toutes les fonctionnalités fournies par VersatileThermostat. L'entité climate VersatileThermostat pilotera votre entité climate, en la coupant si les fenêtres sont ouvertes, la passant en mode Eco si personne n'est présent, etc. Cf. [ici](#pourquoi-une-nouvelle-implémentation-du-thermostat). Pour ce type de thermostat, les cycles éventuels de chauffe sont pilotés par l'entité climate sous-jacente et pas par le Versatile Thermostat lui-même.
|
||||
|
||||
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.
|
||||
|
||||
@@ -100,28 +107,44 @@ La configuration peut être modifiée via la même interface. Sélectionnez simp
|
||||
|
||||
Suivez ensuite les étapes de configuration comme suit :
|
||||
|
||||
## Configuration minimale
|
||||
## Choix des attributs de base
|
||||
|
||||

|
||||
|
||||
Donnez les principaux attributs obligatoires :
|
||||
1. un nom (sera le nom de l'intégration et aussi le nom de l'entité climate)
|
||||
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)
|
||||
2. le type de thermostat ```thermostat_over_switch``` pour piloter un radiateur commandé par un switch ou ```thermostat_over_climate``` pour piloter un autre thermostat. Cf. [ci-dessus](#pourquoi-une-nouvelle-implémentation-du-thermostat)
|
||||
4. un identifiant d'entité de capteur de température qui donne la température de la pièce dans laquelle le radiateur est installé,
|
||||
5. 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
|
||||
6. une durée de cycle en minutes. A chaque cycle, le radiateur s'allumera puis s'éteindra pendant une durée calculée afin d'atteindre la température ciblée (voir [preset](#configure-the-preset-temperature) ci-dessous),
|
||||
7. les températures minimales et maximales du thermostat,
|
||||
8. la liste des fonctionnalités qui seront utilisées pour ce thermostat. En fonction de vos choix, les écrans de configuration suivants s'afficheront ou pas.
|
||||
|
||||
>  _*Notes*_
|
||||
1. 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**,
|
||||
1. avec le type ```thermostat_over_swutch```, 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
|
||||
|
||||
## Sélectionnez l'entité pilotée
|
||||
En fonction de votre choix sur le type de thermostat, vous devrez choisir une entité de type switch ou une entité de type climate. Seules les entités compatibles sont présentées.
|
||||
|
||||
Pour un thermostat de type ```thermostat_over_switch```:
|
||||

|
||||
L'algorithme à utiliser est aujourd'hui limité à TPI est disponible. Voir [algorithme](#algorithme)
|
||||
|
||||
Pour un thermostat de type ```thermostat_over_climate```:
|
||||

|
||||
|
||||
## Configurez les coefficients de l'algorithme TPI
|
||||
|
||||
Cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
Si vous avez choisi un thermostat de type ```thermostat_over_switch``` vous arriverez sur cette page :
|
||||
|
||||

|
||||
|
||||
Vous devez donner :
|
||||
1. le coefficient coef_int de l'algorithme TPI,
|
||||
2. le coefficient coef_ext de l'algorithme TPI
|
||||
|
||||
|
||||
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
|
||||
@@ -144,7 +167,7 @@ Le mode préréglé (preset) vous permet de préconfigurer la température cibl
|
||||
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 :
|
||||
Si vous avez choisi la fonctionnalité ```Avec détection des ouvertures```, cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
@@ -159,7 +182,7 @@ Et c'est tout ! votre thermostat s'éteindra lorsque les fenêtres seront ouvert
|
||||
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 :
|
||||
Si vous avez choisi la fonctionnalité ```Avec détection de mouvement```, cliquez sur 'Valider' sur la page précédente et vous y arriverez :
|
||||
|
||||

|
||||
|
||||
@@ -181,9 +204,9 @@ Pour que cela fonctionne, le thermostat climatique doit être en mode prérégl
|
||||
>  _*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
|
||||
## Configurer la gestion de la puissance
|
||||
|
||||
Cliquez sur 'Valider' sur la page précédente et vous arriverez ici :
|
||||
Si vous avez choisi la fonctionnalité ```Avec détection de la puissance```, cliquez sur 'Valider' sur la page précédente et vous arriverez ici :
|
||||
|
||||

|
||||
|
||||
@@ -193,13 +216,13 @@ Notez que toutes les valeurs de puissance doivent avoir les mêmes unités (kW o
|
||||
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.
|
||||
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).
|
||||
Si sélectionnée en première page, 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 :
|
||||
|
||||

|
||||
|
||||
50
README.md
50
README.md
@@ -14,6 +14,7 @@
|
||||
- [Manual installation](#manual-installation)
|
||||
- [Configuration](#configuration)
|
||||
- [Minimal configuration update](#minimal-configuration-update)
|
||||
- [Select the driven entity](#select-the-driven-entity)
|
||||
- [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)
|
||||
@@ -40,18 +41,22 @@
|
||||
- [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
|
||||
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),
|
||||
3. an external temperature sensor (think of the meteo integration if you don't have one)
|
||||
This thermostat can control 2 types of equipment:
|
||||
1. a heater that only works in on/off mode (named ```thermostat_over_switch```). The minimum configuration required to use this type of thermostat is:
|
||||
has. equipment such as a radiator (a ```switch``` or equivalent),
|
||||
b. a temperature probe for the room (or an input_number),
|
||||
vs. an external temperature sensor (think about weather integration if you don't have one)
|
||||
2. another thermostat that has its own operating modes (named ```thermostat_over_climate```). For this type of thermostat, the minimum configuration requires:
|
||||
has. equipment such as air conditioning which is controlled by its own ```climate``` type entity,
|
||||
b. a temperature probe for the room (or an input_number),
|
||||
vs. an external temperature sensor (think about weather integration if you don't have one)
|
||||
|
||||
Because this integration aims to command the heater considering the preset configured and the room temperature, those informations are mandatory.
|
||||
The ```thermostat_over_climate``` type allows you to add all the functionality provided by VersatileThermostat to your existing equipment. The climate VersatileThermostat entity will control your climate entity, turning it off if the windows are open, switching it to Eco mode if no one is present, etc. See [here](#why-a-new-implementation-of-the-thermostat). For this type of thermostat, any heating cycles are controlled by the underlying climate entity and not by the Versatile Thermostat itself.
|
||||
|
||||
# Why another thermostat implementation ?
|
||||
|
||||
@@ -103,17 +108,28 @@ Then follow the configurations steps as follow:
|
||||

|
||||
|
||||
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 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)
|
||||
1. a name (will be the name of the integration and also the name of the climate entity)
|
||||
2. the type of thermostat ```thermostat_over_switch``` to control a radiator controlled by a switch or ```thermostat_over_climate``` to control another thermostat. Cf. [above](#why-a-new-thermostat-implementation)
|
||||
4. a temperature sensor entity identifier which gives the temperature of the room in which the radiator is installed,
|
||||
5. a temperature sensor entity giving the outside temperature. If you don't have an external sensor, you can use local weather integration
|
||||
6. a cycle duration in minutes. On each cycle, the heater will cycle on and then off for a calculated time to reach the target temperature (see [preset](#configure-the-preset-temperature) below),
|
||||
7. minimum and maximum thermostat temperatures,
|
||||
8. the list of features that will be used for this thermostat. Depending on your choices, the following configuration screens will appear or not.
|
||||
|
||||
>  _*Notes*_
|
||||
1. 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**,
|
||||
1. With the ```thermostat_over_switch``` type, calculation are done at each cycle. So in case of conditions change, you will have to wait for the next cycle to see a change. For this reason, the cycle should not be too long. **5 min is a good value**,
|
||||
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
|
||||
|
||||
## Select the driven entity
|
||||
Depending on your choice on the type of thermostat, you will have to choose a switch type entity or a climate type entity. Only compatible entities are shown.
|
||||
|
||||
For a ```thermostat_over_switch``` thermostat:
|
||||

|
||||
The algorithm to be used today is limited to TPI is available. See [algorithm](#algorithm)
|
||||
|
||||
For a ```thermostat_over_climate``` thermostat:
|
||||

|
||||
|
||||
## Configure the TPI algorithm coefficients
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||

|
||||
@@ -139,7 +155,7 @@ The preset mode allows you to pre-configurate targeted temperature. Used in conj
|
||||
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:
|
||||
If you choose the ```Window management``` feature, click on 'Validate' on the previous page and you will get there:
|
||||

|
||||
|
||||
Give the following attributes:
|
||||
@@ -153,7 +169,7 @@ And that's it ! your thermostat will turn off when the windows is open and be tu
|
||||
2. If you don't have any window/door sensor in your room, just leave the sensor entity id empty
|
||||
|
||||
## Configure the activity mode or motion detection
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||
If you choose the ```Motion management``` feature, lick on 'Validate' on the previous page and you will get there:
|
||||

|
||||
|
||||
We will now see how to configure the new Activity mode.
|
||||
@@ -176,7 +192,7 @@ For this to work, the climate thermostat should be in ``Activity`` preset mode.
|
||||
|
||||
## Configure the power management
|
||||
|
||||
Click on 'Validate' on the previous page and you will get there:
|
||||
If you choose the ```Power management``` feature, click on 'Validate' on the previous page and you will get there:
|
||||

|
||||
|
||||
This feature allows you to regulate the power consumption of your radiators. Known as shedding, this feature allows you to limit the electrical power consumption of your heater if overpowering conditions are detected. Give a **sensor to the current power consumption of your house**, a **sensor to the max power** that should not be exceeded, the **power consumption of your heater** and the algorithm will not start a radiator if the max power will be exceeded after radiator starts.
|
||||
@@ -192,7 +208,7 @@ This allows you to change the max power along time using a Scheduler or whatever
|
||||
4. If you don't want to use this feature, just leave the entities id empty
|
||||
|
||||
## 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).
|
||||
If you choose the ```Presence management``` feature, 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:
|
||||
|
||||

|
||||
|
||||
37
container
Executable file
37
container
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# set -x
|
||||
|
||||
. .bashrc
|
||||
|
||||
cd $HA
|
||||
|
||||
echo "arguments are: "$*
|
||||
# Post installation of container
|
||||
command=$1
|
||||
if [ "$command" == "install" ]; then
|
||||
echo "Running container post installation"
|
||||
script/setup
|
||||
fi
|
||||
|
||||
if [ "$command" == "start" ]; then
|
||||
echo "Running container start"
|
||||
hass -c ./config --debug
|
||||
fi
|
||||
|
||||
if [ "$command" == "translations" ]; then
|
||||
echo "Running container start"
|
||||
python3 -m script.translations develop
|
||||
fi
|
||||
|
||||
if [ "$command" == "hassfest" ]; then
|
||||
echo "Running container start"
|
||||
python3 -m script.hassfest
|
||||
fi
|
||||
|
||||
if [ "$command" == "restart" ]; then
|
||||
echo "Killing existing container"
|
||||
pkill hass
|
||||
echo "Killing existing container"
|
||||
hass -c ./config
|
||||
fi
|
||||
1
custom_components/homeassistant
Symbolic link
1
custom_components/homeassistant
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/vscode/core/homeassistant
|
||||
@@ -29,20 +29,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
# hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
# TODO 1. Create API instance
|
||||
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
|
||||
if api is None:
|
||||
api = VersatileThermostatAPI(hass)
|
||||
|
||||
# TODO 2. Validate the API connection (and authentication)
|
||||
# TODO 3. Store an API object for your platforms to access
|
||||
api.add_entry(entry)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
api: VersatileThermostatAPI = hass.data.get(DOMAIN)
|
||||
@@ -89,3 +93,21 @@ class VersatileThermostatAPI(Dict):
|
||||
def hass(self):
|
||||
"""Get the HomeAssistant object"""
|
||||
return self._hass
|
||||
|
||||
|
||||
# Example migration function
|
||||
async def async_migrate_entry(hass, config_entry: ConfigEntry):
|
||||
"""Migrate old entry."""
|
||||
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||
|
||||
if config_entry.version == 1:
|
||||
|
||||
new = {**config_entry.data}
|
||||
# TODO: modify Config Entry data
|
||||
|
||||
config_entry.version = 2
|
||||
hass.config_entries.async_update_entry(config_entry, data=new)
|
||||
|
||||
_LOGGER.info("Migration to version %s successful", config_entry.version)
|
||||
|
||||
return True
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,13 @@
|
||||
"""Config flow for Versatile Thermostat integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
import logging
|
||||
import copy
|
||||
from collections.abc import Mapping
|
||||
import voluptuous as vol
|
||||
|
||||
from typing import Any
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.config_entries import (
|
||||
@@ -15,12 +16,26 @@ from homeassistant.config_entries import (
|
||||
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
|
||||
from homeassistant.data_entry_flow import FlowHandler, FlowResult
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.input_boolean import (
|
||||
DOMAIN as INPUT_BOOLEAN_DOMAIN,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.components.input_number import (
|
||||
DOMAIN as INPUT_NUMBER_DOMAIN,
|
||||
)
|
||||
|
||||
from homeassistant.components.person import DOMAIN as PERSON_DOMAIN
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
@@ -51,12 +66,17 @@ from .const import (
|
||||
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_CLIMATE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
CONF_THERMOSTAT_TYPES,
|
||||
UnknownEntity,
|
||||
)
|
||||
|
||||
from .climate import VersatileThermostat
|
||||
|
||||
# from .climate import VersatileThermostat
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -101,6 +121,35 @@ def add_suggested_values_to_schema(
|
||||
return vol.Schema(schema)
|
||||
|
||||
|
||||
# def is_temperature_sensor(sensor: RegistryEntry):
|
||||
# """Check if a registryEntry is a temperature sensor or assimilable to a temperature sensor"""
|
||||
# if not sensor.entity_id.startswith(
|
||||
# INPUT_NUMBER_DOMAIN
|
||||
# ) and not sensor.entity_id.startswith(SENSOR_DOMAIN):
|
||||
# return False
|
||||
# return (
|
||||
# sensor.device_class == TEMPERATURE
|
||||
# or sensor.original_device_class == TEMPERATURE
|
||||
# or sensor.unit_of_measurement in TEMPERATURE_UNITS
|
||||
# )
|
||||
#
|
||||
#
|
||||
# def is_power_sensor(sensor: RegistryEntry):
|
||||
# """Check if a registryEntry is a power sensor or assimilable to a temperature sensor"""
|
||||
# if not sensor.entity_id.startswith(
|
||||
# INPUT_NUMBER_DOMAIN
|
||||
# ) and not sensor.entity_id.startswith(SENSOR_DOMAIN):
|
||||
# return False
|
||||
# return (
|
||||
# sensor.unit_of_measurement
|
||||
# in [
|
||||
# UnitOfPower.KILO_WATT,
|
||||
# UnitOfPower.WATT,
|
||||
# UnitOfPower.BTU_PER_HOUR,
|
||||
# ]
|
||||
# )
|
||||
|
||||
|
||||
class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""The base Config flow class. Used to put some code in commons."""
|
||||
|
||||
@@ -111,13 +160,97 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
super().__init__()
|
||||
_LOGGER.debug("CTOR BaseConfigFlow infos: %s", infos)
|
||||
self._infos = infos
|
||||
is_empty: bool = not bool(infos)
|
||||
# Fix features selection depending to infos
|
||||
self._infos[CONF_USE_WINDOW_FEATURE] = (
|
||||
is_empty or self._infos.get(CONF_WINDOW_SENSOR) is not None
|
||||
)
|
||||
self._infos[CONF_USE_MOTION_FEATURE] = (
|
||||
is_empty or self._infos.get(CONF_MOTION_SENSOR) is not None
|
||||
)
|
||||
self._infos[CONF_USE_POWER_FEATURE] = is_empty or (
|
||||
self._infos.get(CONF_POWER_SENSOR) is not None
|
||||
and self._infos.get(CONF_MAX_POWER_SENSOR) is not None
|
||||
)
|
||||
self._infos[CONF_USE_PRESENCE_FEATURE] = (
|
||||
is_empty or self._infos.get(CONF_PRESENCE_SENSOR) is not None
|
||||
)
|
||||
|
||||
# self.hass = async_get_hass()
|
||||
# ent_reg = async_get(hass=self.hass)
|
||||
|
||||
# climates = []
|
||||
# switches = []
|
||||
# temp_sensors = []
|
||||
# power_sensors = []
|
||||
# window_sensors = []
|
||||
# presence_sensors = []
|
||||
#
|
||||
# k: str
|
||||
# for k in ent_reg.entities:
|
||||
# v: RegistryEntry = ent_reg.entities[k]
|
||||
# _LOGGER.debug("Looking entity: %s", k)
|
||||
# # if k.startswith(CLIMATE_DOMAIN) and (
|
||||
# # infos is None or k != infos.get("entity_id")
|
||||
# # ):
|
||||
# # _LOGGER.debug("Climate !")
|
||||
# # climates.append(k)
|
||||
# if k.startswith(SWITCH_DOMAIN) or k.startswith(INPUT_BOOLEAN_DOMAIN):
|
||||
# _LOGGER.debug("Switch !")
|
||||
# switches.append(k)
|
||||
# elif is_temperature_sensor(v):
|
||||
# _LOGGER.debug("Temperature sensor !")
|
||||
# temp_sensors.append(k)
|
||||
# elif is_power_sensor(v):
|
||||
# _LOGGER.debug("Power sensor !")
|
||||
# power_sensors.append(k)
|
||||
# elif k.startswith(PERSON_DOMAIN):
|
||||
# _LOGGER.debug("Presence sensor !")
|
||||
# presence_sensors.append(k)
|
||||
#
|
||||
# # window sensor and presence
|
||||
# if k.startswith(INPUT_BOOLEAN_DOMAIN) or k.startswith(BINARY_SENSOR_DOMAIN):
|
||||
# _LOGGER.debug("Window or presence sensor !")
|
||||
# window_sensors.append(k)
|
||||
# presence_sensors.append(k)
|
||||
#
|
||||
# # Special case for climates which are not in EntityRegistry
|
||||
# climates = self.find_all_climates()
|
||||
|
||||
self.STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_HEATER): cv.string,
|
||||
vol.Required(CONF_TEMP_SENSOR): cv.string,
|
||||
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): cv.string,
|
||||
vol.Required(
|
||||
CONF_THERMOSTAT_TYPE, default=CONF_THERMOSTAT_SWITCH
|
||||
): vol.In(CONF_THERMOSTAT_TYPES),
|
||||
vol.Required(CONF_TEMP_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
||||
),
|
||||
),
|
||||
# vol.In(temp_sensors),
|
||||
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
||||
),
|
||||
), # vol.In(temp_sensors),
|
||||
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_PRESENCE_FEATURE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_THERMOSTAT_SWITCH = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HEATER): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
), # vol.In(switches),
|
||||
vol.Required(
|
||||
CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI
|
||||
): vol.In(
|
||||
@@ -125,8 +258,14 @@ 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),
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_THERMOSTAT_CLIMATE = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_CLIMATE): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=CLIMATE_DOMAIN),
|
||||
), # vol.In(climates),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -146,14 +285,22 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
self.STEP_WINDOW_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_WINDOW_SENSOR): cv.string,
|
||||
vol.Optional(CONF_WINDOW_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[BINARY_SENSOR_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
), # vol.In(window_sensors),
|
||||
vol.Optional(CONF_WINDOW_DELAY, default=30): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_MOTION_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_MOTION_SENSOR): cv.string,
|
||||
vol.Optional(CONF_MOTION_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[BINARY_SENSOR_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
), # vol.In(window_sensors),
|
||||
vol.Optional(CONF_MOTION_DELAY, default=30): cv.positive_int,
|
||||
vol.Optional(CONF_MOTION_PRESET, default="comfort"): vol.In(
|
||||
CONF_PRESETS_SELECTIONABLE
|
||||
@@ -166,16 +313,32 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
self.STEP_POWER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_POWER_SENSOR): cv.string,
|
||||
vol.Optional(CONF_MAX_POWER_SENSOR): cv.string,
|
||||
vol.Optional(CONF_DEVICE_POWER): vol.Coerce(float),
|
||||
vol.Optional(CONF_PRESET_POWER): vol.Coerce(float),
|
||||
vol.Optional(CONF_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
||||
),
|
||||
), # vol.In(power_sensors),
|
||||
vol.Optional(CONF_MAX_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]
|
||||
),
|
||||
), # vol.In(power_sensors),
|
||||
vol.Optional(CONF_DEVICE_POWER, default="1"): vol.Coerce(float),
|
||||
vol.Optional(CONF_PRESET_POWER, default="13"): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
self.STEP_PRESENCE_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_PRESENCE_SENSOR): cv.string,
|
||||
vol.Optional(CONF_PRESENCE_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[
|
||||
PERSON_DOMAIN,
|
||||
BINARY_SENSOR_DOMAIN,
|
||||
INPUT_BOOLEAN_DOMAIN,
|
||||
]
|
||||
),
|
||||
), # vol.In(presence_sensors),
|
||||
}
|
||||
).extend(
|
||||
{
|
||||
@@ -209,6 +372,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
CONF_POWER_SENSOR,
|
||||
CONF_MAX_POWER_SENSOR,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
CONF_CLIMATE,
|
||||
]:
|
||||
d = data.get(conf, None) # pylint: disable=invalid-name
|
||||
if d is not None and self.hass.states.get(d) is None:
|
||||
@@ -268,9 +432,25 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_user user_input=%s", user_input)
|
||||
|
||||
return await self.generic_step(
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_tpi
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_type
|
||||
)
|
||||
|
||||
async def async_step_type(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_type user_input=%s", user_input)
|
||||
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_SWITCH:
|
||||
return await self.generic_step(
|
||||
"type", self.STEP_THERMOSTAT_SWITCH, user_input, self.async_step_tpi
|
||||
)
|
||||
else:
|
||||
return await self.generic_step(
|
||||
"type",
|
||||
self.STEP_THERMOSTAT_CLIMATE,
|
||||
user_input,
|
||||
self.async_step_presets,
|
||||
)
|
||||
|
||||
async def async_step_tpi(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_tpi user_input=%s", user_input)
|
||||
@@ -283,35 +463,63 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the presets flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_presets user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_WINDOW_FEATURE]:
|
||||
next_step = self.async_step_window
|
||||
elif self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, self.async_step_window
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_window(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the window sensor flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_window user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, self.async_step_motion
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_motion(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the window and motion sensor flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_motion user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, self.async_step_power
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_power(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the power management flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_power user_input=%s", user_input)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"power",
|
||||
self.STEP_POWER_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_step_presence,
|
||||
next_step,
|
||||
)
|
||||
|
||||
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -336,6 +544,21 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
self.async_finalize, # pylint: disable=no-member
|
||||
)
|
||||
|
||||
async def async_finalize(self):
|
||||
"""Should be implemented by Leaf classes"""
|
||||
raise HomeAssistantError(
|
||||
"async_finalize not implemented on VersatileThermostat sub-class"
|
||||
)
|
||||
|
||||
def find_all_climates(self) -> list(str):
|
||||
"""Find all climate known by HA"""
|
||||
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
|
||||
ret: list(str) = list()
|
||||
for entity in component.entities:
|
||||
ret.append(entity.entity_id)
|
||||
_LOGGER.debug("Found all climate entities: %s", ret)
|
||||
return ret
|
||||
|
||||
|
||||
class VersatileThermostatConfigFlow(
|
||||
VersatileThermostatBaseConfigFlow, HAConfigFlow, domain=DOMAIN
|
||||
@@ -359,16 +582,12 @@ class VersatileThermostatConfigFlow(
|
||||
return self.async_create_entry(title=self._infos[CONF_NAME], data=self._infos)
|
||||
|
||||
|
||||
class UnknownEntity(HomeAssistantError):
|
||||
"""Error to indicate there is an unknown entity_id given."""
|
||||
|
||||
|
||||
class VersatileThermostatOptionsFlowHandler(
|
||||
VersatileThermostatBaseConfigFlow, OptionsFlow
|
||||
):
|
||||
"""Handle options flow for Versatile Thermostat integration."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry):
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
super().__init__(config_entry.data.copy())
|
||||
self.config_entry = config_entry
|
||||
@@ -394,9 +613,27 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
)
|
||||
|
||||
return await self.generic_step(
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_tpi
|
||||
"user", self.STEP_USER_DATA_SCHEMA, user_input, self.async_step_type
|
||||
)
|
||||
|
||||
async def async_step_type(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug(
|
||||
"Into OptionsFlowHandler.async_step_user user_input=%s", user_input
|
||||
)
|
||||
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_SWITCH:
|
||||
return await self.generic_step(
|
||||
"type", self.STEP_THERMOSTAT_SWITCH, user_input, self.async_step_tpi
|
||||
)
|
||||
else:
|
||||
return await self.generic_step(
|
||||
"type",
|
||||
self.STEP_THERMOSTAT_CLIMATE,
|
||||
user_input,
|
||||
self.async_step_presets,
|
||||
)
|
||||
|
||||
async def async_step_tpi(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the tpi flow steps"""
|
||||
_LOGGER.debug(
|
||||
@@ -413,8 +650,18 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"Into OptionsFlowHandler.async_step_presets user_input=%s", user_input
|
||||
)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_WINDOW_FEATURE]:
|
||||
next_step = self.async_step_window
|
||||
elif self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, self.async_step_window
|
||||
"presets", self.STEP_PRESETS_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_window(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -423,8 +670,15 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"Into OptionsFlowHandler.async_step_window user_input=%s", user_input
|
||||
)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
next_step = self.async_step_motion
|
||||
elif self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
return await self.generic_step(
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, self.async_step_motion
|
||||
"window", self.STEP_WINDOW_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_motion(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -433,8 +687,14 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"Into OptionsFlowHandler.async_step_motion user_input=%s", user_input
|
||||
)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_POWER_FEATURE]:
|
||||
next_step = self.async_step_power
|
||||
elif self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, self.async_step_power
|
||||
"motion", self.STEP_MOTION_DATA_SCHEMA, user_input, next_step
|
||||
)
|
||||
|
||||
async def async_step_power(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -443,11 +703,15 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
"Into OptionsFlowHandler.async_step_power user_input=%s", user_input
|
||||
)
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
if self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
return await self.generic_step(
|
||||
"power",
|
||||
self.STEP_POWER_DATA_SCHEMA,
|
||||
user_input,
|
||||
self.async_step_presence,
|
||||
next_step,
|
||||
)
|
||||
|
||||
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||
@@ -478,34 +742,20 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
|
||||
async def async_end(self):
|
||||
"""Finalization of the ConfigEntry creation"""
|
||||
_LOGGER.debug(
|
||||
"CTOR ConfigFlow.async_finalize - updating entry with: %s", self._infos
|
||||
if not self._infos[CONF_USE_WINDOW_FEATURE]:
|
||||
self._infos[CONF_WINDOW_SENSOR] = None
|
||||
if not self._infos[CONF_USE_MOTION_FEATURE]:
|
||||
self._infos[CONF_MOTION_SENSOR] = None
|
||||
if not self._infos[CONF_USE_POWER_FEATURE]:
|
||||
self._infos[CONF_POWER_SENSOR] = None
|
||||
self._infos[CONF_MAX_POWER_SENSOR] = None
|
||||
if not self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
self._infos[CONF_PRESENCE_SENSOR] = None
|
||||
|
||||
_LOGGER.info(
|
||||
"Recreating entry %s due to configuration change. New config is now: %s",
|
||||
self.config_entry.entry_id,
|
||||
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)
|
||||
|
||||
@@ -9,6 +9,8 @@ from homeassistant.components.climate.const import (
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .prop_algorithm import (
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
)
|
||||
@@ -42,6 +44,14 @@ 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_THERMOSTAT_TYPE = "thermostat_type"
|
||||
CONF_THERMOSTAT_SWITCH = "thermostat_over_switch"
|
||||
CONF_THERMOSTAT_CLIMATE = "thermostat_over_climate"
|
||||
CONF_CLIMATE = "climate_entity_id"
|
||||
CONF_USE_WINDOW_FEATURE = "use_window_feature"
|
||||
CONF_USE_MOTION_FEATURE = "use_motion_feature"
|
||||
CONF_USE_PRESENCE_FEATURE = "use_presence_feature"
|
||||
CONF_USE_POWER_FEATURE = "use_power_feature"
|
||||
|
||||
CONF_PRESETS = {
|
||||
p: f"{p}_temp"
|
||||
@@ -92,6 +102,14 @@ ALL_CONF = (
|
||||
CONF_TEMP_MIN,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_SECURITY_DELAY_MIN,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_CLIMATE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
]
|
||||
+ CONF_PRESETS_VALUES
|
||||
+ CONF_PRESETS_AWAY_VALUES,
|
||||
@@ -101,7 +119,13 @@ CONF_FUNCTIONS = [
|
||||
PROPORTIONAL_FUNCTION_TPI,
|
||||
]
|
||||
|
||||
CONF_THERMOSTAT_TYPES = [CONF_THERMOSTAT_SWITCH, CONF_THERMOSTAT_CLIMATE]
|
||||
|
||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
|
||||
|
||||
SERVICE_SET_PRESENCE = "set_presence"
|
||||
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
||||
|
||||
|
||||
class UnknownEntity(HomeAssistantError):
|
||||
"""Error to indicate there is an unknown entity_id given."""
|
||||
|
||||
@@ -8,13 +8,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"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)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entity",
|
||||
"description": "Linked entity attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -97,13 +109,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"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)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entity",
|
||||
"description": "Linked entity attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -177,5 +201,25 @@
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"climate": {
|
||||
"versatile_thermostat": {
|
||||
"states_attributes": {
|
||||
"preset_mode": {
|
||||
"power": "Shedding",
|
||||
"security": "Security"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"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)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entity",
|
||||
"description": "Linked entity attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -97,13 +109,25 @@
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"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)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
"temp_max": "Maximal temperature allowed"
|
||||
"temp_max": "Maximal temperature allowed",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entity",
|
||||
"description": "Linked entity attributes",
|
||||
"data": {
|
||||
"heater_entity_id": "Heater entity id",
|
||||
"proportional_function": "Algorithm to use (TPI is the only one for now)",
|
||||
"climate_entity_id": "Underlying thermostat entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -177,5 +201,25 @@
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_over_switch": "Thermostat over a switch",
|
||||
"thermostat_over_climate": "Thermostat over another thermostat"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"climate": {
|
||||
"versatile_thermostat": {
|
||||
"states_attributes": {
|
||||
"preset_mode": {
|
||||
"power": "Shedding",
|
||||
"security": "Security"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,24 @@
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
"name": "Nom",
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"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)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
"temp_max": "Température maximale permise",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entité liée",
|
||||
"description": "Attributs de l'entité liée",
|
||||
"data": {
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"proportional_function": "Algorithme à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"climate_entity_id": "Thermostat sous-jacent entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -97,13 +108,26 @@
|
||||
"description": "Principaux attributs obligatoires",
|
||||
"data": {
|
||||
"name": "Nom",
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat",
|
||||
"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)",
|
||||
"temp_min": "Température minimale permise",
|
||||
"temp_max": "Température maximale permise"
|
||||
"temp_max": "Température maximale permise",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entité liée",
|
||||
"description": "Attributs de l'entité liée",
|
||||
"data": {
|
||||
"heater_entity_id": "Radiateur entity id",
|
||||
"proportional_function": "Algorithme à utiliser (Seul TPI est disponible pour l'instant)",
|
||||
"climate_entity_id": "Thermostat sous-jacent entity id"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -177,5 +201,25 @@
|
||||
"abort": {
|
||||
"already_configured": "Le device est déjà configuré"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"thermostat_type": {
|
||||
"options": {
|
||||
"thermostat_over_switch": "Thermostat sur un switch",
|
||||
"thermostat_over_climate": "Thermostat sur un autre thermostat"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"climate": {
|
||||
"versatile_thermostat": {
|
||||
"states_attributes": {
|
||||
"preset_mode": {
|
||||
"power": "Délestage",
|
||||
"security": "Sécurité"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
images/config-linked-entity.png
Normal file
BIN
images/config-linked-entity.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
images/config-linked-entity2.png
Normal file
BIN
images/config-linked-entity2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 14 KiB |
Reference in New Issue
Block a user