2.0.0-beta1

Issue #30 - security mode
Issue #26 - upodate entity on config change
Issue #21 - check box to select feature
Issue #16 - Suggest/select entity in a list on the configuration screens
Issue #5  - Thermostat over climate type
This commit is contained in:
Jean-Marc Collin
2023-01-29 19:53:49 +01:00
parent 4786949aeb
commit b607fb1075
15 changed files with 789 additions and 328 deletions

6
.bashrc Normal file
View File

@@ -0,0 +1,6 @@
echo "Sourcing .bashrc"
alias ll='ls -l'
export HA='/home/vscode/core'
cd $HA
source venv/bin/activate

View File

@@ -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.
@@ -95,4 +101,13 @@ climate:
- platform: generic_thermostat
name: Underlying thermostat9
heater: input_boolean.fake_heater_switch3
target_sensor: input_number.fake_temperature_sensor1
target_sensor: input_number.fake_temperature_sensor1
recorder:
include:
domains:
- input_boolean
- input_number
- switch
- climate
- sensor

View File

@@ -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
View File

@@ -11,9 +11,13 @@
"host": "localhost",
"justMyCode": false,
"pathMappings": [
// {
// "localRoot": "${workspaceFolder}",
// "remoteRoot": "."
//},
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
"localRoot": "${workspaceFolder}/../core",
"remoteRoot": "/home/vscode/core"
}
]
},

View File

@@ -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
View File

@@ -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": []
}
]

37
container Executable file
View 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

View File

@@ -0,0 +1 @@
/home/vscode/core/homeassistant

View File

@@ -93,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

View File

@@ -7,6 +7,9 @@ import copy
from collections.abc import Mapping
import voluptuous as vol
from homeassistant.exceptions import HomeAssistantError
from homeassistant.const import TEMPERATURE, UnitOfPower
from homeassistant.util.unit_system import TEMPERATURE_UNITS
from homeassistant.core import callback, async_get_hass
from homeassistant.config_entries import (
@@ -17,27 +20,27 @@ from homeassistant.config_entries import (
from homeassistant.data_entry_flow import FlowHandler
from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_registry import EntityRegistry, async_get
from homeassistant.components.climate import ClimateEntity
from homeassistant.helpers.entity_registry import (
RegistryEntry,
async_get,
)
from homeassistant.components.climate.const import DOMAIN as CLIMATE_DOMAIN
from homeassistant.components.switch import SwitchEntity
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.switch.const import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.input_boolean import (
InputBoolean,
DOMAIN as INPUT_BOOLEAN_DOMAIN,
)
from homeassistant.components.sensor import SensorEntity
from homeassistant.components.sensor.const import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.input_number import (
InputNumber,
DOMAIN as INPUT_NUMBER_DOMAIN,
)
from homeassistant.components.person import DOMAIN as PERSON_DOMAIN
from .const import (
DOMAIN,
@@ -76,6 +79,7 @@ from .const import (
CONF_USE_PRESENCE_FEATURE,
CONF_USE_POWER_FEATURE,
CONF_THERMOSTAT_TYPES,
UnknownEntity,
)
_LOGGER = logging.getLogger(__name__)
@@ -122,6 +126,37 @@ 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.device_class == TEMPERATURE
# or sensor.original_device_class == TEMPERATURE
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."""
@@ -135,22 +170,43 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
self.hass = async_get_hass()
ent_reg = async_get(hass=self.hass)
climates = [] # self.find_all_climates()
switches = [] # self.find_all_heaters()
temp_sensors = [] # self.find_all_temperature_sensors()
climates = []
switches = []
temp_sensors = []
power_sensors = []
window_sensors = []
presence_sensors = []
k: str
for k in ent_reg.entities:
v = ent_reg.entities[k]
if k.startswith(CLIMATE_DOMAIN):
climates.append(k)
elif k.startswith(SWITCH_DOMAIN) or k.startswith(INPUT_BOOLEAN_DOMAIN):
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 k.startswith(INPUT_NUMBER_DOMAIN):
temp_sensors.append(k)
elif k.startswith(SENSOR_DOMAIN):
_LOGGER.debug("We have found sensor: %s", v)
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
if k.startswith(INPUT_BOOLEAN_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(
{
@@ -160,6 +216,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
): vol.In(CONF_THERMOSTAT_TYPES),
vol.Required(CONF_TEMP_SENSOR): vol.In(temp_sensors),
vol.Required(CONF_EXTERNAL_TEMP_SENSOR): 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,
@@ -179,7 +236,6 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
PROPORTIONAL_FUNCTION_TPI,
]
),
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
}
)
@@ -205,14 +261,14 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
self.STEP_WINDOW_DATA_SCHEMA = vol.Schema(
{
vol.Optional(CONF_WINDOW_SENSOR): cv.string,
vol.Optional(CONF_WINDOW_SENSOR): 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): 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
@@ -225,16 +281,16 @@ 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): vol.In(power_sensors),
vol.Optional(CONF_MAX_POWER_SENSOR): 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): vol.In(presence_sensors),
}
).extend(
{
@@ -455,30 +511,6 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
_LOGGER.debug("Found all climate entities: %s", ret)
return ret
def find_all_heaters(self) -> list(str):
"""Find all heater known by HA"""
component: EntityComponent[SwitchEntity] = self.hass.data[SWITCH_DOMAIN]
ret: list(str) = list()
for entity in component.entities:
ret.append(entity.entity_id)
# component = self.hass.data[INPUT_BOOLEAN_DOMAIN]
# for entity in component.entities:
# ret.append(entity.entity_id)
_LOGGER.debug("Found all switch entities: %s", ret)
return ret
def find_all_temperature_sensors(self) -> list(str):
"""Find all heater known by HA"""
component: EntityComponent[SensorEntity] = self.hass.data[SENSOR_DOMAIN]
ret: list(str) = list()
for entity in component.entities:
ret.append(entity.entity_id)
# component = self.hass.data[INPUT_NUMBER_DOMAIN]
# for entity in component.entities:
# ret.append(entity.entity_id)
_LOGGER.debug("Found all temperature sensore entities: %s", ret)
return ret
class VersatileThermostatConfigFlow(
VersatileThermostatBaseConfigFlow, HAConfigFlow, domain=DOMAIN
@@ -502,16 +534,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
@@ -537,9 +565,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(

View File

@@ -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,
)
@@ -123,3 +125,7 @@ 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."""

View File

@@ -9,10 +9,9 @@
"data": {
"name": "Name",
"thermostat_type": "Thermostat type",
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat",
"temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"use_window_feature": "Use window detection",
@@ -22,10 +21,13 @@
}
},
"type": {
"heater_entity_id": "Heater entity id",
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"cycle_min": "Cycle duration (minutes)",
"climate_entity_id": "Underlying thermostat entity id"
"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": {
"title": "TPI",
@@ -91,6 +93,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id"
@@ -108,10 +118,9 @@
"data": {
"name": "Name",
"thermostat_type": "Thermostat type",
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat",
"temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"use_window_feature": "Use window detection",
@@ -121,10 +130,13 @@
}
},
"type": {
"heater_entity_id": "Heater entity id",
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"cycle_min": "Cycle duration (minutes)",
"climate_entity_id": "Underlying thermostat entity id"
"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": {
"title": "TPI",
@@ -190,6 +202,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id"

View File

@@ -9,10 +9,9 @@
"data": {
"name": "Name",
"thermostat_type": "Thermostat type",
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat",
"temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"use_window_feature": "Use window detection",
@@ -22,10 +21,13 @@
}
},
"type": {
"heater_entity_id": "Heater entity id",
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"cycle_min": "Cycle duration (minutes)",
"climate_entity_id": "Underlying thermostat entity id"
"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": {
"title": "TPI",
@@ -91,6 +93,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id"
@@ -108,10 +118,9 @@
"data": {
"name": "Name",
"thermostat_type": "Thermostat type",
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat",
"temperature_sensor_entity_id": "Temperature sensor entity id",
"external_temperature_sensor_entity_id": "External temperature sensor entity id",
"cycle_min": "Cycle duration (minutes)",
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"use_window_feature": "Use window detection",
@@ -121,10 +130,13 @@
}
},
"type": {
"heater_entity_id": "Heater entity id",
"proportional_function": "Algorithm to use (TPI is the only one for now)",
"cycle_min": "Cycle duration (minutes)",
"climate_entity_id": "Underlying thermostat entity id"
"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": {
"title": "TPI",
@@ -190,6 +202,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat over a switch",
"thermostat_over_climate": "Thermostat over another thermostat"
}
}
},
"error": {
"unknown": "Unexpected error",
"unknown_entity": "Unknown entity id"

View File

@@ -8,8 +8,6 @@
"description": "Principaux attributs obligatoires",
"data": {
"name": "Nom",
"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)",
@@ -22,10 +20,13 @@
}
},
"type": {
"heater_entity_id": "Radiateur entity id",
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
"cycle_min": "Durée du cycle (minutes)",
"climate_entity_id": "Thermostat sous-jacent entity id"
"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": {
"title": "TPI",
@@ -91,6 +92,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat sur un switch",
"thermostat_over_climate": "Thermostat sur un autre thermostat"
}
}
},
"error": {
"unknown": "Erreur inattendue",
"unknown_entity": "entity id inconnu"
@@ -121,10 +130,13 @@
}
},
"type": {
"heater_entity_id": "Radiateur entity id",
"proportional_function": "Algorithm à utiliser (Seul TPI est disponible pour l'instant)",
"cycle_min": "Durée du cycle (minutes)",
"climate_entity_id": "Thermostat sous-jacent entity id"
"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": {
"title": "TPI",
@@ -190,6 +202,14 @@
}
}
},
"selectors": {
"thermostat_type": {
"options": {
"thermostat_over_switch": "Thermostat sur un switch",
"thermostat_over_climate": "Thermostat sur un autre thermostat"
}
}
},
"error": {
"unknown": "Erreur inattendue",
"unknown_entity": "entity id inconnu"