Compare commits
5 Commits
1.0.0beta2
...
1.1.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
638e007c21 | ||
|
|
c520d7fad5 | ||
|
|
c366314a95 | ||
|
|
6223177918 | ||
|
|
2bd6b7093e |
100
README.md
100
README.md
@@ -5,11 +5,37 @@
|
|||||||
|
|
||||||
>  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 ;-).
|
>  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)
|
||||||
|
- [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)]._
|
_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.
|
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:
|
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),
|
1. an equipement like a heater (a switch),
|
||||||
2. a temperature sensor for the room (or an input_number),
|
2. a temperature sensor for the room (or an input_number),
|
||||||
@@ -17,7 +43,7 @@ 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.
|
Because this integration aims to command the heater considering the preset configured and the room temperature, those informations are mandatory.
|
||||||
|
|
||||||
## Why another thermostat implementation ?
|
# Why another thermostat implementation ?
|
||||||
|
|
||||||
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".
|
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 :
|
This component named __Versatile thermostat__ manage the following use cases :
|
||||||
@@ -31,9 +57,9 @@ This component named __Versatile thermostat__ manage the following use cases :
|
|||||||
- Add **home presence management**. This feature allows you to dynamically change the temperature of preset considering a occupancy sensor of your home.
|
- 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.
|
- 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.
|
||||||
|
|
||||||
## How to install this incredible Versatile Thermostat ?
|
# How to install this incredible Versatile Thermostat ?
|
||||||
|
|
||||||
### HACS installation (recommended)
|
## HACS installation (recommended)
|
||||||
|
|
||||||
1. Install [HACS](https://hacs.xyz/). That way you get updates automatically.
|
1. Install [HACS](https://hacs.xyz/). That way you get updates automatically.
|
||||||
2. Add this Github repository as custom repository in HACS settings.
|
2. Add this Github repository as custom repository in HACS settings.
|
||||||
@@ -41,7 +67,7 @@ This component named __Versatile thermostat__ manage the following use cases :
|
|||||||
4. Restart Home Assistant,
|
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)
|
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`).
|
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.
|
2. If you do not have a `custom_components` directory (folder) there, you need to create it.
|
||||||
@@ -52,7 +78,7 @@ This component named __Versatile thermostat__ manage the following use cases :
|
|||||||
7. Configure new Versatile Thermostat integration
|
7. Configure new Versatile Thermostat integration
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
# Configuration
|
||||||
|
|
||||||
Note: no configuration in configuration.yaml is needed because all configuration is done through the standard GUI when adding the integration.
|
Note: no configuration in configuration.yaml is needed because all configuration is done through the standard GUI when adding the integration.
|
||||||
|
|
||||||
@@ -63,7 +89,7 @@ The configuration can be change through the same interface. Simply select the th
|
|||||||
|
|
||||||
Then follow the configurations steps as follow:
|
Then follow the configurations steps as follow:
|
||||||
|
|
||||||
### Minimal configuration update
|
## Minimal configuration update
|
||||||

|

|
||||||
|
|
||||||
Give the main mandatory attributes:
|
Give the main mandatory attributes:
|
||||||
@@ -78,13 +104,13 @@ Give the main mandatory attributes:
|
|||||||
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. 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
|
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
|
## Configure the TPI algorithm coefficients
|
||||||
Click on 'Validate' on the previous page and you will get there:
|
Click on 'Validate' on the previous page and you will get there:
|
||||||

|

|
||||||
|
|
||||||
For more informations on the TPI algorithm and tuned please refer to [algorithm](#algorithm).
|
For more informations on the TPI algorithm and tuned please refer to [algorithm](#algorithm).
|
||||||
|
|
||||||
### Configure the preset temperature
|
## Configure the preset temperature
|
||||||
Click on 'Validate' on the previous page and you will get there:
|
Click on 'Validate' on the previous page and you will get there:
|
||||||

|

|
||||||
|
|
||||||
@@ -101,7 +127,7 @@ The preset mode allows you to pre-configurate targeted temperature. Used in conj
|
|||||||
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).
|
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 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
|
4. If 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
|
## Configure the doors/windows turning on/off the thermostats
|
||||||
Click on 'Validate' on the previous page and you will get there:
|
Click on 'Validate' on the previous page and you will get there:
|
||||||

|

|
||||||
|
|
||||||
@@ -115,7 +141,7 @@ And that's it ! your thermostat will turn off when the windows is open and be tu
|
|||||||
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/)
|
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
|
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
|
## Configure the activity mode or motion detection
|
||||||
Click on 'Validate' on the previous page and you will get there:
|
Click on 'Validate' on the previous page and you will get there:
|
||||||

|

|
||||||
|
|
||||||
@@ -137,7 +163,7 @@ For this to work, the climate thermostat should be in ``Activity`` preset mode.
|
|||||||
>  _*Notes*_
|
>  _*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
|
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
|
## Configure the power management
|
||||||
Click on 'Validate' on the previous page and you will get there:
|
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.
|
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.
|
||||||
@@ -153,7 +179,7 @@ This allows you to change the max power along time using a Scheduler or whatever
|
|||||||
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.
|
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
|
4. If you don't want to use this feature, just leave the entities id empty
|
||||||
|
|
||||||
### Configure the presence or occupancy
|
## 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).
|
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).
|
||||||
|
|
||||||
For this you need to configure:
|
For this you need to configure:
|
||||||
@@ -166,7 +192,7 @@ For this you need to configure:
|
|||||||
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,
|
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.
|
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.
|
||||||
|
|
||||||
## Algorithm
|
# 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 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.
|
This algorithm make the temperature converge and stop oscillating.
|
||||||
@@ -189,7 +215,43 @@ To tune those coefficients keep in mind that:
|
|||||||
|
|
||||||
See some situations at [examples](#some-results).
|
See some situations at [examples](#some-results).
|
||||||
|
|
||||||
### Custom attributes
|
# 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:
|
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:
|
||||||

|

|
||||||
@@ -226,7 +288,7 @@ Custom attributes are the following:
|
|||||||
| ``friendly_name`` | The name of the thermostat |
|
| ``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 |
|
| ``supported_features`` | A combination of all features supported by this thermostat. See official climate integration documentation for more informations |
|
||||||
|
|
||||||
### Some results
|
# Some results
|
||||||
|
|
||||||
Convergence of temperature to target configured by preset:
|
Convergence of temperature to target configured by preset:
|
||||||

|

|
||||||
@@ -243,6 +305,8 @@ Algorithm calculation evolution
|
|||||||
|
|
||||||
Enjoy !
|
Enjoy !
|
||||||
|
|
||||||
|
# Even better
|
||||||
|
|
||||||
## Even Better with Scheduler Component !
|
## 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
|
In order to enjoy the full power of Versatile Thermostat, I invite you to use it with https://github.com/nielsfaber/scheduler-component
|
||||||
@@ -298,7 +362,7 @@ Example configuration:
|
|||||||
name: Porte sam
|
name: Porte sam
|
||||||
```
|
```
|
||||||
|
|
||||||
## Even-Even-Even better with Apex-chart to tune your Thermostat
|
## 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):
|
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):
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -337,7 +401,7 @@ series:
|
|||||||
yaxis_id: right
|
yaxis_id: right
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributions are welcome!
|
# Contributions are welcome!
|
||||||
|
|
||||||
If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)
|
If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ from .const import (
|
|||||||
SERVICE_SET_PRESENCE,
|
SERVICE_SET_PRESENCE,
|
||||||
SERVICE_SET_PRESET_TEMPERATURE,
|
SERVICE_SET_PRESET_TEMPERATURE,
|
||||||
PRESET_AWAY_SUFFIX,
|
PRESET_AWAY_SUFFIX,
|
||||||
|
CONF_SECURITY_DELAY_MIN,
|
||||||
|
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||||
|
CONF_TEMP_MAX,
|
||||||
|
CONF_TEMP_MIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .prop_algorithm import PropAlgorithm
|
from .prop_algorithm import PropAlgorithm
|
||||||
@@ -133,6 +137,10 @@ async def async_setup_entry(
|
|||||||
tpi_coef_ext = entry.data.get(CONF_TPI_COEF_EXT)
|
tpi_coef_ext = entry.data.get(CONF_TPI_COEF_EXT)
|
||||||
presence_sensor_entity_id = entry.data.get(CONF_PRESENCE_SENSOR)
|
presence_sensor_entity_id = entry.data.get(CONF_PRESENCE_SENSOR)
|
||||||
power_temp = entry.data.get(CONF_PRESET_POWER)
|
power_temp = entry.data.get(CONF_PRESET_POWER)
|
||||||
|
temp_min = entry.data.get(CONF_TEMP_MIN)
|
||||||
|
temp_max = entry.data.get(CONF_TEMP_MAX)
|
||||||
|
security_delay_min = entry.data.get(CONF_SECURITY_DELAY_MIN)
|
||||||
|
minimal_activation_delay = entry.data.get(CONF_MINIMAL_ACTIVATION_DELAY)
|
||||||
|
|
||||||
presets = {}
|
presets = {}
|
||||||
for (key, value) in CONF_PRESETS.items():
|
for (key, value) in CONF_PRESETS.items():
|
||||||
@@ -161,6 +169,8 @@ async def async_setup_entry(
|
|||||||
proportional_function,
|
proportional_function,
|
||||||
temp_sensor_entity_id,
|
temp_sensor_entity_id,
|
||||||
ext_temp_sensor_entity_id,
|
ext_temp_sensor_entity_id,
|
||||||
|
temp_min,
|
||||||
|
temp_max,
|
||||||
power_sensor_entity_id,
|
power_sensor_entity_id,
|
||||||
max_power_sensor_entity_id,
|
max_power_sensor_entity_id,
|
||||||
window_sensor_entity_id,
|
window_sensor_entity_id,
|
||||||
@@ -176,6 +186,8 @@ async def async_setup_entry(
|
|||||||
tpi_coef_ext,
|
tpi_coef_ext,
|
||||||
presence_sensor_entity_id,
|
presence_sensor_entity_id,
|
||||||
power_temp,
|
power_temp,
|
||||||
|
security_delay_min,
|
||||||
|
minimal_activation_delay,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
True,
|
True,
|
||||||
@@ -223,6 +235,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
proportional_function,
|
proportional_function,
|
||||||
temp_sensor_entity_id,
|
temp_sensor_entity_id,
|
||||||
ext_temp_sensor_entity_id,
|
ext_temp_sensor_entity_id,
|
||||||
|
temp_min,
|
||||||
|
temp_max,
|
||||||
power_sensor_entity_id,
|
power_sensor_entity_id,
|
||||||
max_power_sensor_entity_id,
|
max_power_sensor_entity_id,
|
||||||
window_sensor_entity_id,
|
window_sensor_entity_id,
|
||||||
@@ -238,6 +252,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
tpi_coef_ext,
|
tpi_coef_ext,
|
||||||
presence_sensor_entity_id,
|
presence_sensor_entity_id,
|
||||||
power_temp,
|
power_temp,
|
||||||
|
security_delay_min,
|
||||||
|
minimal_activation_delay,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the thermostat."""
|
"""Initialize the thermostat."""
|
||||||
|
|
||||||
@@ -253,6 +269,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._proportional_function = proportional_function
|
self._proportional_function = proportional_function
|
||||||
self._temp_sensor_entity_id = temp_sensor_entity_id
|
self._temp_sensor_entity_id = temp_sensor_entity_id
|
||||||
self._ext_temp_sensor_entity_id = ext_temp_sensor_entity_id
|
self._ext_temp_sensor_entity_id = ext_temp_sensor_entity_id
|
||||||
|
self._attr_max_temp = temp_max
|
||||||
|
self._attr_min_temp = temp_min
|
||||||
self._power_sensor_entity_id = power_sensor_entity_id
|
self._power_sensor_entity_id = power_sensor_entity_id
|
||||||
self._max_power_sensor_entity_id = max_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_sensor_entity_id = window_sensor_entity_id
|
||||||
@@ -273,7 +291,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._presence_sensor_entity_id = presence_sensor_entity_id
|
self._presence_sensor_entity_id = presence_sensor_entity_id
|
||||||
self._power_temp = power_temp
|
self._power_temp = power_temp
|
||||||
|
|
||||||
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:
|
# TODO if self.ac_mode:
|
||||||
# self.hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
|
# self.hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
|
||||||
@@ -335,14 +353,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
)
|
)
|
||||||
self._tpi_coef_ext = 0
|
self._tpi_coef_ext = 0
|
||||||
|
|
||||||
|
self._security_delay_min = security_delay_min
|
||||||
|
self._minimal_activation_delay = 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
|
# Initiate the ProportionalAlgorithm
|
||||||
self._prop_algorithm = PropAlgorithm(
|
self._prop_algorithm = PropAlgorithm(
|
||||||
self._proportional_function,
|
self._proportional_function,
|
||||||
self._tpi_coef_int,
|
self._tpi_coef_int,
|
||||||
self._tpi_coef_ext,
|
self._tpi_coef_ext,
|
||||||
self._cycle_min,
|
self._cycle_min,
|
||||||
|
self._minimal_activation_delay,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._async_cancel_cycle = None
|
self._async_cancel_cycle = None
|
||||||
self._window_call_cancel = None
|
self._window_call_cancel = None
|
||||||
self._motion_call_cancel = None
|
self._motion_call_cancel = None
|
||||||
@@ -455,14 +480,15 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
||||||
if hvac_mode == HVAC_MODE_HEAT:
|
if hvac_mode == HVAC_MODE_HEAT:
|
||||||
self._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:
|
elif hvac_mode == HVAC_MODE_COOL:
|
||||||
self._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:
|
elif hvac_mode == HVAC_MODE_OFF:
|
||||||
self._hvac_mode = HVAC_MODE_OFF
|
self._hvac_mode = HVAC_MODE_OFF
|
||||||
if self._is_device_active:
|
if self._is_device_active:
|
||||||
await self._async_heater_turn_off()
|
await self._async_heater_turn_off()
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
else:
|
else:
|
||||||
_LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
|
_LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
|
||||||
return
|
return
|
||||||
@@ -472,7 +498,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
async def async_set_preset_mode(self, preset_mode):
|
async def async_set_preset_mode(self, preset_mode):
|
||||||
"""Set new preset mode."""
|
"""Set new preset mode."""
|
||||||
await self._async_set_preset_mode_internal(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):
|
async def _async_set_preset_mode_internal(self, preset_mode, force=False):
|
||||||
"""Set new preset mode."""
|
"""Set new preset mode."""
|
||||||
@@ -553,6 +579,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._target_temp = temperature
|
self._target_temp = temperature
|
||||||
self._attr_preset_mode = PRESET_NONE
|
self._attr_preset_mode = PRESET_NONE
|
||||||
self.recalculate()
|
self.recalculate()
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
async def entry_update_listener(
|
async def entry_update_listener(
|
||||||
@@ -638,21 +665,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self.async_startup()
|
await self.async_startup()
|
||||||
|
|
||||||
# starts the cycle
|
# starts the cycle
|
||||||
if self._cycle_min:
|
# if self._cycle_min:
|
||||||
self.async_on_remove(
|
# self.async_on_remove(
|
||||||
async_track_time_interval(
|
# async_track_time_interval(
|
||||||
self.hass,
|
# self.hass,
|
||||||
self._async_control_heating,
|
# self._async_control_heating,
|
||||||
interval=timedelta(minutes=self._cycle_min),
|
# interval=timedelta(minutes=self._cycle_min),
|
||||||
)
|
# )
|
||||||
)
|
# )
|
||||||
|
|
||||||
async def async_startup(self):
|
async def async_startup(self):
|
||||||
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
||||||
_LOGGER.debug("%s - Calling async_startup", self)
|
_LOGGER.debug("%s - Calling async_startup", self)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_startup_internal(*_):
|
async def _async_startup_internal(*_):
|
||||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||||
need_write_state = False
|
need_write_state = False
|
||||||
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
|
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
|
||||||
@@ -665,7 +692,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self,
|
self,
|
||||||
float(temperature_state.state),
|
float(temperature_state.state),
|
||||||
)
|
)
|
||||||
self._async_update_temp(temperature_state)
|
await self._async_update_temp(temperature_state)
|
||||||
need_write_state = True
|
need_write_state = True
|
||||||
|
|
||||||
if self._ext_temp_sensor_entity_id:
|
if self._ext_temp_sensor_entity_id:
|
||||||
@@ -681,7 +708,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self,
|
self,
|
||||||
float(ext_temperature_state.state),
|
float(ext_temperature_state.state),
|
||||||
)
|
)
|
||||||
self._async_update_ext_temp(ext_temperature_state)
|
await self._async_update_ext_temp(ext_temperature_state)
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
|
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
|
||||||
@@ -788,7 +815,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self.get_my_previous_state()
|
await self.get_my_previous_state()
|
||||||
|
|
||||||
if self.hass.state == CoreState.running:
|
if self.hass.state == CoreState.running:
|
||||||
_async_startup_internal()
|
await _async_startup_internal()
|
||||||
else:
|
else:
|
||||||
self.hass.bus.async_listen_once(
|
self.hass.bus.async_listen_once(
|
||||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||||
@@ -867,8 +894,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._async_update_temp(new_state)
|
await self._async_update_temp(new_state)
|
||||||
self.recalculate()
|
self.recalculate()
|
||||||
|
await self._async_control_heating(force=False)
|
||||||
|
|
||||||
async def _async_ext_temperature_changed(self, event):
|
async def _async_ext_temperature_changed(self, event):
|
||||||
"""Handle external temperature changes."""
|
"""Handle external temperature changes."""
|
||||||
@@ -881,8 +909,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._async_update_ext_temp(new_state)
|
await self._async_update_ext_temp(new_state)
|
||||||
self.recalculate()
|
self.recalculate()
|
||||||
|
await self._async_control_heating(force=False)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
async def _async_windows_changed(self, event):
|
async def _async_windows_changed(self, event):
|
||||||
@@ -992,6 +1021,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# We do not change the preset which is kept to ACTIVITY but only the target_temperature
|
# We do not change the preset which is kept to ACTIVITY but only the target_temperature
|
||||||
self._target_temp = self._presets[new_preset]
|
self._target_temp = self._presets[new_preset]
|
||||||
self.recalculate()
|
self.recalculate()
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
|
|
||||||
if self._motion_call_cancel:
|
if self._motion_call_cancel:
|
||||||
self._motion_call_cancel()
|
self._motion_call_cancel()
|
||||||
@@ -1023,24 +1053,32 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_temp(self, state):
|
async def _async_update_temp(self, state):
|
||||||
"""Update thermostat with latest state from sensor."""
|
"""Update thermostat with latest state from sensor."""
|
||||||
try:
|
try:
|
||||||
cur_temp = float(state.state)
|
cur_temp = float(state.state)
|
||||||
if math.isnan(cur_temp) or math.isinf(cur_temp):
|
if math.isnan(cur_temp) or math.isinf(cur_temp):
|
||||||
raise ValueError(f"Sensor has illegal state {state.state}")
|
raise ValueError(f"Sensor has illegal state {state.state}")
|
||||||
self._cur_temp = cur_temp
|
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)
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
_LOGGER.error("Unable to update temperature from sensor: %s", ex)
|
_LOGGER.error("Unable to update temperature from sensor: %s", ex)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_ext_temp(self, state):
|
async def _async_update_ext_temp(self, state):
|
||||||
"""Update thermostat with latest state from sensor."""
|
"""Update thermostat with latest state from sensor."""
|
||||||
try:
|
try:
|
||||||
cur_ext_temp = float(state.state)
|
cur_ext_temp = float(state.state)
|
||||||
if math.isnan(cur_ext_temp) or math.isinf(cur_ext_temp):
|
if math.isnan(cur_ext_temp) or math.isinf(cur_ext_temp):
|
||||||
raise ValueError(f"Sensor has illegal state {state.state}")
|
raise ValueError(f"Sensor has illegal state {state.state}")
|
||||||
self._cur_ext_temp = cur_ext_temp
|
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)
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
_LOGGER.error("Unable to update external temperature from sensor: %s", ex)
|
_LOGGER.error("Unable to update external temperature from sensor: %s", ex)
|
||||||
|
|
||||||
@@ -1105,6 +1143,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._update_presence(new_state.state)
|
self._update_presence(new_state.state)
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
|
|
||||||
def _update_presence(self, new_state):
|
def _update_presence(self, new_state):
|
||||||
_LOGGER.debug("%s - Updating presence. New state is %s", self, new_state)
|
_LOGGER.debug("%s - Updating presence. New state is %s", self, new_state)
|
||||||
@@ -1229,7 +1268,29 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._saved_preset_mode if self._saved_preset_mode else PRESET_NONE
|
self._saved_preset_mode if self._saved_preset_mode else PRESET_NONE
|
||||||
)
|
)
|
||||||
|
|
||||||
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"""
|
"""The main function used to run the calculation at each cycle"""
|
||||||
|
|
||||||
overpowering: bool = await self.check_overpowering()
|
overpowering: bool = await self.check_overpowering()
|
||||||
@@ -1245,60 +1306,90 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
on_time_sec: int = self._prop_algorithm.on_time_sec
|
on_time_sec: int = self._prop_algorithm.on_time_sec
|
||||||
off_time_sec: int = self._prop_algorithm.off_time_sec
|
off_time_sec: int = self._prop_algorithm.off_time_sec
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - Running new cycle at %s. on_time_sec=%.0f, off_time_sec=%.0f",
|
"%s - Running new cycle at %s. on_time_sec=%.0f, off_time_sec=%.0f, security_state=%s",
|
||||||
self,
|
self,
|
||||||
time,
|
time,
|
||||||
on_time_sec,
|
on_time_sec,
|
||||||
off_time_sec,
|
off_time_sec,
|
||||||
|
self._security_state,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cancel eventual previous cycle if any
|
# Cancel eventual previous cycle if any
|
||||||
if self._async_cancel_cycle is not None:
|
if self._async_cancel_cycle is not None:
|
||||||
_LOGGER.debug(
|
if force:
|
||||||
"%s - A previous cycle is alredy running -> waits for its end", self
|
_LOGGER.debug("%s - we force a new cycle", 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(_):
|
|
||||||
self._async_cancel_cycle()
|
self._async_cancel_cycle()
|
||||||
self._async_cancel_cycle = None
|
self._async_cancel_cycle = None
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s - A previous cycle is alredy running and no force -> waits for its end",
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
self._should_relaunch_control_heating = True
|
||||||
|
return
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if time > 0 and on is True and self.check_date_temperature() is False:
|
||||||
|
_LOGGER.warning("%s - Set the thermostat into security mode")
|
||||||
|
self._security_state = True
|
||||||
|
self._saved_hvac_mode = self.hvac_mode
|
||||||
|
await self.async_set_hvac_mode(HVAC_MODE_OFF)
|
||||||
|
return
|
||||||
|
|
||||||
|
action_label = "start" if on else "stop"
|
||||||
if self._should_relaunch_control_heating:
|
if self._should_relaunch_control_heating:
|
||||||
_LOGGER.debug("Don't stop cause a cycle have to be relaunch")
|
_LOGGER.debug(
|
||||||
|
"Don't %s cause a cycle have to be relaunch", action_label
|
||||||
|
)
|
||||||
self._should_relaunch_control_heating = False
|
self._should_relaunch_control_heating = False
|
||||||
await self._async_control_heating()
|
await self._async_control_heating()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - stop heating for %d min %d sec",
|
"%s - %s heating for %d min %d sec",
|
||||||
self,
|
self,
|
||||||
off_time_sec // 60,
|
action_label,
|
||||||
off_time_sec % 60,
|
time // 60,
|
||||||
|
time % 60,
|
||||||
)
|
)
|
||||||
await self._async_heater_turn_off()
|
if time > 0:
|
||||||
|
await heater_action()
|
||||||
|
else:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s - No action on heater cause duration is 0", self
|
||||||
|
)
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
|
self._async_cancel_cycle = async_call_later(
|
||||||
|
self.hass,
|
||||||
|
time,
|
||||||
|
next_cycle_action,
|
||||||
|
)
|
||||||
|
|
||||||
# Program turn off
|
async def _turn_on_later(_):
|
||||||
self._async_cancel_cycle = async_call_later(
|
await _turn_on_off_later(
|
||||||
self.hass,
|
on=True,
|
||||||
on_time_sec,
|
time=self._prop_algorithm.on_time_sec,
|
||||||
_turn_off,
|
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:
|
elif self._is_device_active:
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
@@ -1352,10 +1443,16 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
"tpi_coef_ext": self._tpi_coef_ext,
|
"tpi_coef_ext": self._tpi_coef_ext,
|
||||||
"saved_preset_mode": self._saved_preset_mode,
|
"saved_preset_mode": self._saved_preset_mode,
|
||||||
"saved_target_temp": self._saved_target_temp,
|
"saved_target_temp": self._saved_target_temp,
|
||||||
|
"saved_hvac_mode": self._saved_hvac_mode,
|
||||||
"window_state": self._window_state,
|
"window_state": self._window_state,
|
||||||
"motion_state": self._motion_state,
|
"motion_state": self._motion_state,
|
||||||
"overpowering_state": self._overpowering_state,
|
"overpowering_state": self._overpowering_state,
|
||||||
"presence_state": self._presence_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(),
|
"last_update_datetime": datetime.now().isoformat(),
|
||||||
}
|
}
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
@@ -1382,6 +1479,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
"""
|
"""
|
||||||
_LOGGER.info("%s - Calling service_set_presence, presence: %s", self, presence)
|
_LOGGER.info("%s - Calling service_set_presence, presence: %s", self, presence)
|
||||||
self._update_presence(presence)
|
self._update_presence(presence)
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
|
|
||||||
async def service_set_preset_temperature(
|
async def service_set_preset_temperature(
|
||||||
self, preset, temperature=None, temperature_away=None
|
self, preset, temperature=None, temperature_away=None
|
||||||
@@ -1417,3 +1515,4 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# If the changed preset is active, change the current temperature
|
# If the changed preset is active, change the current temperature
|
||||||
if self._attr_preset_mode == preset:
|
if self._attr_preset_mode == preset:
|
||||||
await self._async_set_preset_mode_internal(preset, force=True)
|
await self._async_set_preset_mode_internal(preset, force=True)
|
||||||
|
await self._async_control_heating(force=True)
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ from .const import (
|
|||||||
CONF_TPI_COEF_INT,
|
CONF_TPI_COEF_INT,
|
||||||
CONF_PRESENCE_SENSOR,
|
CONF_PRESENCE_SENSOR,
|
||||||
PROPORTIONAL_FUNCTION_TPI,
|
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
|
||||||
@@ -117,6 +121,8 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
PROPORTIONAL_FUNCTION_TPI,
|
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 +180,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]:
|
async def validate_input(self, data: dict) -> dict[str]:
|
||||||
"""Validate the user input allows us to connect.
|
"""Validate the user input allows us to connect.
|
||||||
|
|
||||||
@@ -303,6 +318,17 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
"presence",
|
"presence",
|
||||||
self.STEP_PRESENCE_DATA_SCHEMA,
|
self.STEP_PRESENCE_DATA_SCHEMA,
|
||||||
user_input,
|
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
|
self.async_finalize, # pylint: disable=no-member
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -417,7 +443,7 @@ class VersatileThermostatOptionsFlowHandler(
|
|||||||
"power",
|
"power",
|
||||||
self.STEP_POWER_DATA_SCHEMA,
|
self.STEP_POWER_DATA_SCHEMA,
|
||||||
user_input,
|
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:
|
async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||||
@@ -430,7 +456,20 @@ class VersatileThermostatOptionsFlowHandler(
|
|||||||
"presence",
|
"presence",
|
||||||
self.STEP_PRESENCE_DATA_SCHEMA,
|
self.STEP_PRESENCE_DATA_SCHEMA,
|
||||||
user_input,
|
user_input,
|
||||||
self.async_finalize, # pylint: disable=no-member
|
self.async_step_advanced,
|
||||||
|
)
|
||||||
|
|
||||||
|
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_finalize,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_finalize(self):
|
async def async_finalize(self):
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ CONF_TPI_COEF_INT = "tpi_coef_int"
|
|||||||
CONF_TPI_COEF_EXT = "tpi_coef_ext"
|
CONF_TPI_COEF_EXT = "tpi_coef_ext"
|
||||||
CONF_PRESENCE_SENSOR = "presence_sensor_entity_id"
|
CONF_PRESENCE_SENSOR = "presence_sensor_entity_id"
|
||||||
CONF_PRESET_POWER = "power_temp"
|
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 = {
|
CONF_PRESETS = {
|
||||||
p: f"{p}_temp"
|
p: f"{p}_temp"
|
||||||
@@ -81,6 +85,10 @@ ALL_CONF = (
|
|||||||
CONF_TPI_COEF_INT,
|
CONF_TPI_COEF_INT,
|
||||||
CONF_TPI_COEF_EXT,
|
CONF_TPI_COEF_EXT,
|
||||||
CONF_PRESENCE_SENSOR,
|
CONF_PRESENCE_SENSOR,
|
||||||
|
CONF_MINIMAL_ACTIVATION_DELAY,
|
||||||
|
CONF_TEMP_MIN,
|
||||||
|
CONF_TEMP_MAX,
|
||||||
|
CONF_SECURITY_DELAY_MIN,
|
||||||
]
|
]
|
||||||
+ CONF_PRESETS_VALUES
|
+ CONF_PRESETS_VALUES
|
||||||
+ CONF_PRESETS_AWAY_VALUES,
|
+ CONF_PRESETS_AWAY_VALUES,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -21,20 +20,22 @@ class PropAlgorithm:
|
|||||||
tpi_coef_int,
|
tpi_coef_int,
|
||||||
tpi_coef_ext,
|
tpi_coef_ext,
|
||||||
cycle_min: int,
|
cycle_min: int,
|
||||||
|
minimal_activation_delay: int,
|
||||||
):
|
):
|
||||||
"""Initialisation of the Proportional Algorithm"""
|
"""Initialisation of the Proportional Algorithm"""
|
||||||
_LOGGER.debug(
|
_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,
|
function_type,
|
||||||
tpi_coef_int,
|
tpi_coef_int,
|
||||||
tpi_coef_ext,
|
tpi_coef_ext,
|
||||||
cycle_min,
|
cycle_min,
|
||||||
|
minimal_activation_delay,
|
||||||
)
|
)
|
||||||
# TODO test function_type, bias, cycle_min
|
|
||||||
self._function = function_type
|
self._function = function_type
|
||||||
self._tpi_coef_int = tpi_coef_int
|
self._tpi_coef_int = tpi_coef_int
|
||||||
self._tpi_coef_ext = tpi_coef_ext
|
self._tpi_coef_ext = tpi_coef_ext
|
||||||
self._cycle_min = cycle_min
|
self._cycle_min = cycle_min
|
||||||
|
self._minimal_activation_delay = minimal_activation_delay
|
||||||
self._on_percent = 0
|
self._on_percent = 0
|
||||||
self._on_time_sec = 0
|
self._on_time_sec = 0
|
||||||
self._off_time_sec = self._cycle_min * 60
|
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
|
self._on_time_sec = self._on_percent * self._cycle_min * 60
|
||||||
|
|
||||||
# Do not heat for less than xx sec
|
# Do not heat for less than xx sec
|
||||||
if self._on_time_sec < PROPORTIONAL_MIN_DURATION_SEC:
|
if self._on_time_sec < self._minimal_activation_delay:
|
||||||
_LOGGER.debug(
|
if self._on_time_sec > 0:
|
||||||
"No heating period due to heating period too small (%f < %f)",
|
_LOGGER.info(
|
||||||
self._on_time_sec,
|
"No heating period due to heating period too small (%f < %f)",
|
||||||
PROPORTIONAL_MIN_DURATION_SEC,
|
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._on_time_sec = 0
|
||||||
|
|
||||||
self._off_time_sec = self._cycle_min * 60 - self._on_time_sec
|
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
|
description: Force the presence mode in thermostat
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
multiple: true
|
|
||||||
integration: versatile_thermostat
|
integration: versatile_thermostat
|
||||||
fields:
|
fields:
|
||||||
presence:
|
presence:
|
||||||
@@ -26,7 +25,6 @@ set_preset_temperature:
|
|||||||
description: Change the target temperature of a preset
|
description: Change the target temperature of a preset
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
multiple: true
|
|
||||||
integration: versatile_thermostat
|
integration: versatile_thermostat
|
||||||
fields:
|
fields:
|
||||||
preset:
|
preset:
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||||
"cycle_min": "Cycle duration (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -69,6 +71,14 @@
|
|||||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||||
"boost_away_temp": "Temperature in Boost 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": {
|
"error": {
|
||||||
@@ -91,7 +101,9 @@
|
|||||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||||
"cycle_min": "Cycle duration (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -148,6 +160,14 @@
|
|||||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||||
"boost_away_temp": "Temperature in Boost 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": {
|
"error": {
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||||
"cycle_min": "Cycle duration (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -69,6 +71,14 @@
|
|||||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||||
"boost_away_temp": "Temperature in Boost 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": {
|
"error": {
|
||||||
@@ -91,7 +101,9 @@
|
|||||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
|
||||||
"cycle_min": "Cycle duration (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -148,6 +160,14 @@
|
|||||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||||
"boost_away_temp": "Temperature in Boost 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": {
|
"error": {
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||||
"cycle_min": "Durée du cycle (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -69,6 +71,14 @@
|
|||||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||||
"boost_away_temp": "Température en preset Boost en cas d'absence"
|
"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": {
|
"error": {
|
||||||
@@ -91,7 +101,9 @@
|
|||||||
"temperature_sensor_entity_id": "Température sensor entity id",
|
"temperature_sensor_entity_id": "Température sensor entity id",
|
||||||
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
"external_temperature_sensor_entity_id": "Temperature exterieure sensor entity id",
|
||||||
"cycle_min": "Durée du cycle (minutes)",
|
"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": {
|
"tpi": {
|
||||||
@@ -148,6 +160,14 @@
|
|||||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||||
"boost_away_temp": "Température en preset Boost en cas d'absence"
|
"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": {
|
"error": {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 38 KiB |
Reference in New Issue
Block a user