Make the temperature preset as persistent entities / redesign of the configuration menu (#409)
* HA 2024.2.b4 * Add temp entities initialization * Python12 env rebuild * Init temperature number for central configuration + testus ok * With calculation of VTherm temp entities + test ok * FIX some testus. Some others are still KO * Beers * Update central config Number temp entity * Many but not all testus ok * All testus ok * With central config temp change ok * Cleaning and fixing Issues * Validation tests ok * With new menu. Testus KO * All developped and tests ok * Fix central_config menu * Documentation and release * Fix testus KO * Add log into migration for testu --------- Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""The Versatile Thermostat integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict
|
||||
@@ -8,16 +9,18 @@ import logging
|
||||
import voluptuous as vol
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from homeassistant.const import SERVICE_RELOAD
|
||||
from homeassistant.const import SERVICE_RELOAD, EVENT_HOMEASSISTANT_STARTED
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigType
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, CoreState, callback
|
||||
|
||||
from .base_thermostat import BaseThermostat
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
PLATFORMS,
|
||||
CONFIG_VERSION,
|
||||
CONFIG_MINOR_VERSION,
|
||||
CONF_AUTO_REGULATION_LIGHT,
|
||||
CONF_AUTO_REGULATION_MEDIUM,
|
||||
CONF_AUTO_REGULATION_STRONG,
|
||||
@@ -27,6 +30,13 @@ from .const import (
|
||||
CONF_SAFETY_MODE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
CONF_USE_CENTRAL_BOILER_FEATURE,
|
||||
CONF_POWER_SENSOR,
|
||||
CONF_PRESENCE_SENSOR,
|
||||
)
|
||||
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
@@ -82,15 +92,27 @@ async def async_setup(
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
# L'argument config contient votre fichier configuration.yaml
|
||||
vtherm_config = config.get(DOMAIN)
|
||||
|
||||
if vtherm_config is not None:
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
api.set_global_config(vtherm_config)
|
||||
else:
|
||||
_LOGGER.info("No global config from configuration.yaml available")
|
||||
|
||||
# Listen HA starts to initialize all links between
|
||||
@callback
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.info(
|
||||
"VersatileThermostat - HA is started, initialize all links between VTherm entities"
|
||||
)
|
||||
await api.init_vtherm_links()
|
||||
|
||||
if hass.state == CoreState.running:
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_startup_internal)
|
||||
|
||||
hass.helpers.service.async_register_admin_service(
|
||||
DOMAIN,
|
||||
SERVICE_RELOAD,
|
||||
@@ -114,6 +136,7 @@ async def reload_all_vtherm(hass):
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
if api:
|
||||
await api.reload_central_boiler_entities_list()
|
||||
await api.init_vtherm_links()
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
@@ -134,6 +157,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
await api.reload_central_boiler_entities_list()
|
||||
await api.init_vtherm_links()
|
||||
|
||||
return True
|
||||
|
||||
@@ -148,6 +172,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
if api is not None:
|
||||
await api.reload_central_boiler_entities_list()
|
||||
await api.init_vtherm_links()
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
@@ -165,15 +190,40 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# Example migration function
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
|
||||
"""Migrate old entry."""
|
||||
_LOGGER.debug("Migrating from version %s", config_entry.version)
|
||||
_LOGGER.debug(
|
||||
"Migrating from version %s/%s", config_entry.version, config_entry.minor_version
|
||||
)
|
||||
|
||||
if config_entry.version == 1:
|
||||
if (
|
||||
config_entry.version != CONFIG_VERSION
|
||||
or config_entry.minor_version != CONFIG_MINOR_VERSION
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"Migration to %s/%s is needed", CONFIG_VERSION, CONFIG_MINOR_VERSION
|
||||
)
|
||||
new = {**config_entry.data}
|
||||
# TO DO: modify Config Entry data if there will be something to migrate
|
||||
|
||||
config_entry.version = 2
|
||||
hass.config_entries.async_update_entry(config_entry, data=new)
|
||||
if (
|
||||
config_entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
== CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
):
|
||||
new[CONF_USE_WINDOW_FEATURE] = True
|
||||
new[CONF_USE_MOTION_FEATURE] = True
|
||||
new[CONF_USE_POWER_FEATURE] = new.get(CONF_POWER_SENSOR, None) is not None
|
||||
new[CONF_USE_PRESENCE_FEATURE] = (
|
||||
new.get(CONF_PRESENCE_SENSOR, None) is not None
|
||||
)
|
||||
|
||||
_LOGGER.info("Migration to version %s successful", config_entry.version)
|
||||
new[CONF_USE_CENTRAL_BOILER_FEATURE] = new.get(
|
||||
"add_central_boiler_control", False
|
||||
) or new.get(CONF_USE_CENTRAL_BOILER_FEATURE, False)
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
data=new,
|
||||
version=CONFIG_VERSION,
|
||||
minor_version=CONFIG_MINOR_VERSION,
|
||||
)
|
||||
_LOGGER.info("Migration to version %s successful", config_entry.version)
|
||||
|
||||
return True
|
||||
|
||||
@@ -82,9 +82,9 @@ from .const import (
|
||||
CONF_NO_MOTION_PRESET,
|
||||
CONF_DEVICE_POWER,
|
||||
CONF_PRESETS,
|
||||
CONF_PRESETS_AWAY,
|
||||
CONF_PRESETS_WITH_AC,
|
||||
CONF_PRESETS_AWAY_WITH_AC,
|
||||
# CONF_PRESETS_AWAY,
|
||||
# CONF_PRESETS_WITH_AC,
|
||||
# CONF_PRESETS_AWAY_WITH_AC,
|
||||
CONF_CYCLE_MIN,
|
||||
CONF_PROP_FUNCTION,
|
||||
CONF_TPI_COEF_INT,
|
||||
@@ -111,6 +111,7 @@ from .const import (
|
||||
CONF_USE_POWER_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_TEMP_MIN,
|
||||
HIDDEN_PRESETS,
|
||||
@@ -214,6 +215,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
super().__init__()
|
||||
|
||||
self._hass = hass
|
||||
self._entry_infos = None
|
||||
self._attr_extra_state_attributes = {}
|
||||
|
||||
self._unique_id = unique_id
|
||||
@@ -285,6 +287,15 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._last_central_mode = None
|
||||
self._is_used_by_central_boiler = False
|
||||
|
||||
self._support_flags = None
|
||||
# Preset will be initialized from Number entities
|
||||
self._presets: dict[str, Any] = {} # presets
|
||||
self._presets_away: dict[str, Any] = {} # presets_away
|
||||
|
||||
self._attr_preset_modes: list[str] | None
|
||||
|
||||
self._use_central_config_temperature = False
|
||||
|
||||
self.post_init(entry_infos)
|
||||
|
||||
def clean_central_config_doublon(
|
||||
@@ -307,10 +318,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
if cfg.get(CONF_USE_TPI_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_TPI_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_PRESETS_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_PRESETS_DATA_SCHEMA)
|
||||
clean_one(cfg, STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA)
|
||||
|
||||
if cfg.get(CONF_USE_WINDOW_CENTRAL_CONFIG) is True:
|
||||
clean_one(cfg, STEP_CENTRAL_WINDOW_DATA_SCHEMA)
|
||||
|
||||
@@ -351,40 +358,22 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
_LOGGER.info("%s - The merged configuration is %s", self, entry_infos)
|
||||
|
||||
self._entry_infos = entry_infos
|
||||
|
||||
self._use_central_config_temperature = entry_infos.get(
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG
|
||||
) or (
|
||||
entry_infos.get(CONF_USE_PRESENCE_CENTRAL_CONFIG)
|
||||
and entry_infos.get(CONF_USE_PRESENCE_FEATURE)
|
||||
)
|
||||
|
||||
self._ac_mode = entry_infos.get(CONF_AC_MODE) is True
|
||||
self._attr_max_temp = entry_infos.get(CONF_TEMP_MAX)
|
||||
self._attr_min_temp = entry_infos.get(CONF_TEMP_MIN)
|
||||
if (step := entry_infos.get(CONF_STEP_TEMPERATURE)) is not None:
|
||||
self._attr_target_temperature_step = step
|
||||
|
||||
# convert entry_infos into usable attributes
|
||||
presets: dict[str, Any] = {}
|
||||
items = CONF_PRESETS_WITH_AC.items() if self._ac_mode else CONF_PRESETS.items()
|
||||
for key, value in items:
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry_infos:
|
||||
presets[key] = entry_infos.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
presets[key] = (
|
||||
self._attr_max_temp if self._ac_mode else self._attr_min_temp
|
||||
)
|
||||
|
||||
presets_away: dict[str, Any] = {}
|
||||
items = (
|
||||
CONF_PRESETS_AWAY_WITH_AC.items()
|
||||
if self._ac_mode
|
||||
else CONF_PRESETS_AWAY.items()
|
||||
)
|
||||
for key, value in items:
|
||||
_LOGGER.debug("looking for key=%s, value=%s", key, value)
|
||||
if value in entry_infos:
|
||||
presets_away[key] = entry_infos.get(value)
|
||||
else:
|
||||
_LOGGER.debug("value %s not found in Entry", value)
|
||||
presets_away[key] = (
|
||||
self._attr_max_temp if self._ac_mode else self._attr_min_temp
|
||||
)
|
||||
self._attr_preset_modes: list[str] | None
|
||||
|
||||
if self._window_call_cancel is not None:
|
||||
self._window_call_cancel()
|
||||
@@ -446,7 +435,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._presence_sensor_entity_id = entry_infos.get(CONF_PRESENCE_SENSOR)
|
||||
self._power_temp = entry_infos.get(CONF_PRESET_POWER)
|
||||
|
||||
self._presence_on = self._presence_sensor_entity_id is not None
|
||||
self._presence_on = (
|
||||
entry_infos.get(CONF_USE_PRESENCE_FEATURE, False)
|
||||
and self._presence_sensor_entity_id is not None
|
||||
)
|
||||
|
||||
if self._ac_mode:
|
||||
# Added by https://github.com/jmcollin78/versatile_thermostat/pull/144
|
||||
@@ -462,15 +454,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
self._support_flags = SUPPORT_FLAGS
|
||||
|
||||
self._presets = presets
|
||||
self._presets_away = presets_away
|
||||
# Preset will be initialized from Number entities
|
||||
self._presets: dict[str, Any] = {} # presets
|
||||
self._presets_away: dict[str, Any] = {} # presets_away
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s - presets are set to: %s, away: %s",
|
||||
self,
|
||||
self._presets,
|
||||
self._presets_away,
|
||||
)
|
||||
# Will be restored if possible
|
||||
self._attr_preset_mode = PRESET_NONE
|
||||
self._saved_preset_mode = PRESET_NONE
|
||||
@@ -534,24 +521,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._overpowering_state = None
|
||||
self._presence_state = None
|
||||
|
||||
# Calculate all possible presets
|
||||
self._attr_preset_modes = [PRESET_NONE]
|
||||
if len(presets):
|
||||
self._support_flags = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE
|
||||
|
||||
for key, _ in CONF_PRESETS.items():
|
||||
if self.find_preset_temp(key) > 0:
|
||||
self._attr_preset_modes.append(key)
|
||||
|
||||
_LOGGER.debug(
|
||||
"After adding presets, preset_modes to %s", self._attr_preset_modes
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug("No preset_modes")
|
||||
|
||||
if self._motion_on:
|
||||
self._attr_preset_modes.append(PRESET_ACTIVITY)
|
||||
|
||||
self._total_energy = 0
|
||||
|
||||
# Read the parameter from configuration.yaml if it exists
|
||||
@@ -802,8 +771,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._hvac_mode or HVACMode.OFF,
|
||||
)
|
||||
|
||||
self.hass.create_task(self._check_initial_state())
|
||||
|
||||
self.reset_last_change_time()
|
||||
|
||||
await self.get_my_previous_state()
|
||||
@@ -852,16 +819,18 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
old_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||
# Never restore a Power or Security preset
|
||||
if (
|
||||
old_preset_mode in self._attr_preset_modes
|
||||
and old_preset_mode not in HIDDEN_PRESETS
|
||||
):
|
||||
if old_preset_mode is not None and old_preset_mode not in HIDDEN_PRESETS:
|
||||
# old_preset_mode in self._attr_preset_modes
|
||||
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE)
|
||||
self.save_preset_mode()
|
||||
else:
|
||||
self._attr_preset_mode = PRESET_NONE
|
||||
|
||||
if not self._hvac_mode and old_state.state:
|
||||
if not self._hvac_mode and old_state.state in [
|
||||
HVACMode.OFF,
|
||||
HVACMode.HEAT,
|
||||
HVACMode.COOL,
|
||||
]:
|
||||
self._hvac_mode = old_state.state
|
||||
else:
|
||||
self._hvac_mode = HVACMode.OFF
|
||||
@@ -1186,6 +1155,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
Is None if the VTherm is not controlled by central_mode"""
|
||||
return self._last_central_mode
|
||||
|
||||
@property
|
||||
def use_central_config_temperature(self):
|
||||
"""True if this VTHerm uses the central configuration temperature"""
|
||||
return self._use_central_config_temperature
|
||||
|
||||
def underlying_entity_id(self, index=0) -> str | None:
|
||||
"""The climate_entity_id. Added for retrocompatibility reason"""
|
||||
if index < self.nb_underlying_entities:
|
||||
@@ -1204,18 +1178,22 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Turn auxiliary heater on."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@overrides
|
||||
async def async_turn_aux_heat_on(self) -> None:
|
||||
"""Turn auxiliary heater on."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@overrides
|
||||
def turn_aux_heat_off(self) -> None:
|
||||
"""Turn auxiliary heater off."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@overrides
|
||||
async def async_turn_aux_heat_off(self) -> None:
|
||||
"""Turn auxiliary heater off."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@overrides
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode, need_control_heating=True):
|
||||
"""Set new target hvac mode."""
|
||||
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
|
||||
@@ -1274,7 +1252,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
f"Got unsupported preset_mode {preset_mode}. Must be one of {self._attr_preset_modes}" # pylint: disable=line-too-long
|
||||
)
|
||||
|
||||
if preset_mode == self._attr_preset_mode and not force:
|
||||
old_preset_mode = self._attr_preset_mode
|
||||
if preset_mode == old_preset_mode and not force:
|
||||
# I don't think we need to call async_write_ha_state if we didn't change the state
|
||||
return
|
||||
|
||||
@@ -1307,8 +1286,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
if overwrite_saved_preset:
|
||||
self.save_preset_mode()
|
||||
|
||||
self.recalculate()
|
||||
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||
# Notify only if there was a real change
|
||||
if self._attr_preset_mode != old_preset_mode:
|
||||
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||
|
||||
def reset_last_change_time(
|
||||
self, old_preset_mode: str | None = None
|
||||
@@ -1323,9 +1305,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._attr_preset_mode not in HIDDEN_PRESETS
|
||||
and old_preset_mode not in HIDDEN_PRESETS
|
||||
):
|
||||
self._last_temperature_measure = (
|
||||
self._last_ext_temperature_measure
|
||||
) = datetime.now(tz=self._current_tz)
|
||||
self._last_temperature_measure = self._last_ext_temperature_measure = (
|
||||
datetime.now(tz=self._current_tz)
|
||||
)
|
||||
|
||||
def find_preset_temp(self, preset_mode: str):
|
||||
"""Find the right temperature of a preset considering the presence if configured"""
|
||||
@@ -1343,11 +1325,15 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
if preset_mode == PRESET_POWER:
|
||||
return self._power_temp
|
||||
if preset_mode == PRESET_ACTIVITY:
|
||||
return self._presets[
|
||||
motion_preset = (
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
]
|
||||
)
|
||||
if motion_preset in self._presets:
|
||||
return self._presets[motion_preset]
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
# Select _ac presets if in COOL Mode (or over_switch with _ac_mode)
|
||||
if self._ac_mode and self._hvac_mode == HVACMode.COOL:
|
||||
@@ -1355,13 +1341,22 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
_LOGGER.info("%s - find preset temp: %s", self, preset_mode)
|
||||
|
||||
temp_val = self._presets.get(preset_mode, 0)
|
||||
if not self._presence_on or self._presence_state in [
|
||||
None,
|
||||
STATE_ON,
|
||||
STATE_HOME,
|
||||
]:
|
||||
return self._presets[preset_mode]
|
||||
return temp_val
|
||||
else:
|
||||
return self._presets_away[self.get_preset_away_name(preset_mode)]
|
||||
# We should return the preset_away temp val but if
|
||||
# preset temp is 0, that means the user don't want to use
|
||||
# the preset so we return 0, even if there is a value is preset_away
|
||||
return (
|
||||
self._presets_away.get(self.get_preset_away_name(preset_mode), 0)
|
||||
if temp_val > 0
|
||||
else temp_val
|
||||
)
|
||||
|
||||
def get_preset_away_name(self, preset_mode: str) -> str:
|
||||
"""Get the preset name in away mode (when presence is off)"""
|
||||
@@ -1398,7 +1393,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Set the target temperature and the target temperature of underlying climate if any
|
||||
For testing purpose you can pass an event_timestamp.
|
||||
"""
|
||||
self._target_temp = temperature
|
||||
if temperature:
|
||||
self._target_temp = temperature
|
||||
return
|
||||
|
||||
def get_state_date_or_now(self, state: State) -> datetime:
|
||||
@@ -1811,11 +1807,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
return
|
||||
|
||||
await self._async_internal_set_temperature(
|
||||
self._presets[
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
]
|
||||
self._presets.get(
|
||||
(
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
),
|
||||
None,
|
||||
)
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"%s - regarding motion, target_temp have been set to %.2f",
|
||||
@@ -2431,21 +2430,21 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"type": self._thermostat_type,
|
||||
"is_controlled_by_central_mode": self.is_controlled_by_central_mode,
|
||||
"last_central_mode": self.last_central_mode,
|
||||
"frost_temp": self._presets[PRESET_FROST_PROTECTION],
|
||||
"eco_temp": self._presets[PRESET_ECO],
|
||||
"boost_temp": self._presets[PRESET_BOOST],
|
||||
"comfort_temp": self._presets[PRESET_COMFORT],
|
||||
"frost_temp": self._presets.get(PRESET_FROST_PROTECTION, 0),
|
||||
"eco_temp": self._presets.get(PRESET_ECO, 0),
|
||||
"boost_temp": self._presets.get(PRESET_BOOST, 0),
|
||||
"comfort_temp": self._presets.get(PRESET_COMFORT, 0),
|
||||
"frost_away_temp": self._presets_away.get(
|
||||
self.get_preset_away_name(PRESET_FROST_PROTECTION)
|
||||
self.get_preset_away_name(PRESET_FROST_PROTECTION), 0
|
||||
),
|
||||
"eco_away_temp": self._presets_away.get(
|
||||
self.get_preset_away_name(PRESET_ECO)
|
||||
self.get_preset_away_name(PRESET_ECO), 0
|
||||
),
|
||||
"boost_away_temp": self._presets_away.get(
|
||||
self.get_preset_away_name(PRESET_BOOST)
|
||||
self.get_preset_away_name(PRESET_BOOST), 0
|
||||
),
|
||||
"comfort_away_temp": self._presets_away.get(
|
||||
self.get_preset_away_name(PRESET_COMFORT)
|
||||
self.get_preset_away_name(PRESET_COMFORT), 0
|
||||
),
|
||||
"power_temp": self._power_temp,
|
||||
"target_temperature_step": self.target_temperature_step,
|
||||
@@ -2626,8 +2625,78 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
def send_event(self, event_type: EventType, data: dict):
|
||||
"""Send an event"""
|
||||
send_vtherm_event(self._hass, event_type=event_type, entity=self, data=data)
|
||||
# _LOGGER.info("%s - Sending event %s with data: %s", self, event_type, data)
|
||||
# data["entity_id"] = self.entity_id
|
||||
# data["name"] = self.name
|
||||
# data["state_attributes"] = self.state_attributes
|
||||
# self._hass.bus.fire(event_type.value, data)
|
||||
|
||||
async def init_presets(self, central_config):
|
||||
"""Init all presets of the VTherm"""
|
||||
# If preset central config is used and central config is set , take the presets from central config
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api()
|
||||
|
||||
presets: dict[str, Any] = {}
|
||||
presets_away: dict[str, Any] = {}
|
||||
|
||||
def calculate_presets(items, use_central_conf_key):
|
||||
presets: dict[str, Any] = {}
|
||||
config_id = self._unique_id
|
||||
if (
|
||||
central_config
|
||||
and self._entry_infos.get(use_central_conf_key, False) is True
|
||||
):
|
||||
config_id = central_config.entry_id
|
||||
|
||||
for key, preset_name in items:
|
||||
_LOGGER.debug("looking for key=%s, preset_name=%s", key, preset_name)
|
||||
value = vtherm_api.get_temperature_number_value(
|
||||
config_id=config_id, preset_name=preset_name
|
||||
)
|
||||
if value is not None:
|
||||
presets[key] = value
|
||||
else:
|
||||
_LOGGER.debug("preset_name %s not found in VTherm API", preset_name)
|
||||
presets[key] = (
|
||||
self._attr_max_temp if self._ac_mode else self._attr_min_temp
|
||||
)
|
||||
return presets
|
||||
|
||||
# Calculate all presets
|
||||
presets = calculate_presets(
|
||||
CONF_PRESETS_WITH_AC.items() if self._ac_mode else CONF_PRESETS.items(),
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
if self._entry_infos.get(CONF_USE_PRESENCE_FEATURE) is True:
|
||||
presets_away = calculate_presets(
|
||||
(
|
||||
CONF_PRESETS_AWAY_WITH_AC.items()
|
||||
if self._ac_mode
|
||||
else CONF_PRESETS_AWAY.items()
|
||||
),
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
)
|
||||
|
||||
# aggregate all available presets now
|
||||
self._presets: dict[str, Any] = presets
|
||||
self._presets_away: dict[str, Any] = presets_away
|
||||
|
||||
# Calculate all possible presets
|
||||
self._attr_preset_modes = [PRESET_NONE]
|
||||
if len(self._presets):
|
||||
self._support_flags = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE
|
||||
|
||||
for key, _ in CONF_PRESETS.items():
|
||||
if self.find_preset_temp(key) > 0:
|
||||
self._attr_preset_modes.append(key)
|
||||
|
||||
_LOGGER.debug(
|
||||
"After adding presets, preset_modes to %s", self._attr_preset_modes
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug("No preset_modes")
|
||||
|
||||
if self._motion_on:
|
||||
self._attr_preset_modes.append(PRESET_ACTIVITY)
|
||||
|
||||
# Re-applicate the last preset if any to take change into account
|
||||
if self._attr_preset_mode:
|
||||
await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
|
||||
|
||||
self.hass.create_task(self._check_initial_state())
|
||||
|
||||
@@ -7,11 +7,11 @@ from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
callback,
|
||||
Event,
|
||||
CoreState,
|
||||
# CoreState,
|
||||
HomeAssistantError,
|
||||
)
|
||||
|
||||
from homeassistant.const import STATE_ON, STATE_OFF, EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.const import STATE_ON, STATE_OFF # , EVENT_HOMEASSISTANT_START
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
@@ -386,17 +386,18 @@ class CentralBoilerBinarySensor(BinarySensorEntity):
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self._hass)
|
||||
api.register_central_boiler(self)
|
||||
|
||||
@callback
|
||||
async def _async_startup_internal(*_):
|
||||
_LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
await self.listen_nb_active_vtherm_entity()
|
||||
|
||||
if self.hass.state == CoreState.running:
|
||||
await _async_startup_internal()
|
||||
else:
|
||||
self.hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
)
|
||||
# Should be not more needed and replaced by vtherm_api.init_vtherm_links
|
||||
# @callback
|
||||
# async def _async_startup_internal(*_):
|
||||
# _LOGGER.debug("%s - Calling async_startup_internal", self)
|
||||
# await self.listen_nb_active_vtherm_entity()
|
||||
#
|
||||
# if self.hass.state == CoreState.running:
|
||||
# await _async_startup_internal()
|
||||
# else:
|
||||
# self.hass.bus.async_listen_once(
|
||||
# EVENT_HOMEASSISTANT_START, _async_startup_internal
|
||||
# )
|
||||
|
||||
async def listen_nb_active_vtherm_entity(self):
|
||||
"""Initialize the listening of state change of VTherms"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" Implements the VersatileThermostat climate component """
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
@@ -44,9 +45,6 @@ from .thermostat_valve import ThermostatOverValve
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# _LOGGER.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" Some usefull commons class """
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
|
||||
import logging
|
||||
@@ -182,6 +183,9 @@ class VersatileThermostatBaseEntity(Entity):
|
||||
"""Returns my climate if found"""
|
||||
if not self._my_climate:
|
||||
self._my_climate = self.find_my_versatile_thermostat()
|
||||
if self._my_climate:
|
||||
# Only the first time
|
||||
self.my_climate_is_initialized()
|
||||
return self._my_climate
|
||||
|
||||
@property
|
||||
@@ -231,13 +235,18 @@ class VersatileThermostatBaseEntity(Entity):
|
||||
)
|
||||
)
|
||||
else:
|
||||
_LOGGER.warning("%s - no entity to listen. Try later", self)
|
||||
_LOGGER.debug("%s - no entity to listen. Try later", self)
|
||||
self._cancel_call = async_call_later(
|
||||
self.hass, timedelta(seconds=1), try_find_climate
|
||||
)
|
||||
|
||||
await try_find_climate(None)
|
||||
|
||||
@callback
|
||||
def my_climate_is_initialized(self):
|
||||
"""Called when the associated climate is initialized"""
|
||||
return
|
||||
|
||||
@callback
|
||||
async def async_my_climate_changed(
|
||||
self, event: Event
|
||||
|
||||
@@ -74,7 +74,9 @@ def add_suggested_values_to_schema(
|
||||
class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""The base Config flow class. Used to put some code in commons."""
|
||||
|
||||
VERSION = 1
|
||||
VERSION = CONFIG_VERSION
|
||||
MINOR_VERSION = CONFIG_MINOR_VERSION
|
||||
|
||||
_infos: dict
|
||||
_placeholders = {
|
||||
CONF_NAME: "",
|
||||
@@ -95,16 +97,22 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
self._init_feature_flags(infos)
|
||||
self._init_central_config_flags(infos)
|
||||
|
||||
def _init_feature_flags(self, infos):
|
||||
def _init_feature_flags(self, _):
|
||||
"""Fix features selection depending to infos"""
|
||||
is_empty: bool = not bool(infos)
|
||||
is_empty: bool = False # TODO remove this not bool(infos)
|
||||
is_central_config = (
|
||||
self._infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
)
|
||||
|
||||
self._infos[CONF_USE_WINDOW_FEATURE] = (
|
||||
is_empty
|
||||
or self._infos.get(CONF_WINDOW_SENSOR) is not None
|
||||
or self._infos.get(CONF_WINDOW_AUTO_OPEN_THRESHOLD) is not None
|
||||
)
|
||||
self._infos[CONF_USE_MOTION_FEATURE] = (
|
||||
is_empty or self._infos.get(CONF_MOTION_SENSOR) is not None
|
||||
is_empty
|
||||
or self._infos.get(CONF_MOTION_SENSOR) is not None
|
||||
or is_central_config
|
||||
)
|
||||
self._infos[CONF_USE_POWER_FEATURE] = is_empty or (
|
||||
self._infos.get(CONF_POWER_SENSOR) is not None
|
||||
@@ -114,6 +122,11 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
is_empty or self._infos.get(CONF_PRESENCE_SENSOR) is not None
|
||||
)
|
||||
|
||||
self._infos[CONF_USE_CENTRAL_BOILER_FEATURE] = is_empty or (
|
||||
self._infos.get(CONF_CENTRAL_BOILER_ACTIVATION_SRV) is not None
|
||||
and self._infos.get(CONF_CENTRAL_BOILER_DEACTIVATION_SRV) is not None
|
||||
)
|
||||
|
||||
def _init_central_config_flags(self, infos):
|
||||
"""Initialisation of central configuration flags"""
|
||||
is_empty: bool = not bool(infos)
|
||||
@@ -128,7 +141,10 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG,
|
||||
):
|
||||
if not is_empty:
|
||||
self._infos[config] = self._infos.get(config) is True
|
||||
current_config = self._infos.get(config, None)
|
||||
self._infos[config] = current_config is True or (
|
||||
current_config is None and self._central_config is not None
|
||||
)
|
||||
else:
|
||||
self._infos[config] = self._central_config is not None
|
||||
|
||||
@@ -193,7 +209,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
raise NoCentralConfig(conf)
|
||||
|
||||
# Check the service for central boiler format
|
||||
if self._infos.get(CONF_ADD_CENTRAL_BOILER_CONTROL):
|
||||
if self._infos.get(CONF_USE_CENTRAL_BOILER_FEATURE):
|
||||
for conf in [
|
||||
CONF_CENTRAL_BOILER_ACTIVATION_SRV,
|
||||
CONF_CENTRAL_BOILER_DEACTIVATION_SRV,
|
||||
@@ -203,6 +219,92 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
except ServiceConfigurationError as err:
|
||||
raise ServiceConfigurationError(conf) from err
|
||||
|
||||
def check_config_complete(self, infos) -> bool:
|
||||
"""True if the config is now complete (ie all mandatory attributes are set)"""
|
||||
is_central_config = (
|
||||
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
)
|
||||
if is_central_config:
|
||||
if (
|
||||
infos.get(CONF_NAME) is None
|
||||
or infos.get(CONF_EXTERNAL_TEMP_SENSOR) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if infos.get(CONF_USE_POWER_FEATURE, False) is True and (
|
||||
infos.get(CONF_POWER_SENSOR, None) is None
|
||||
or infos.get(CONF_MAX_POWER_SENSOR, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_PRESENCE_FEATURE, False) is True
|
||||
and infos.get(CONF_PRESENCE_SENSOR, None) is None
|
||||
):
|
||||
return False
|
||||
else:
|
||||
if (
|
||||
infos.get(CONF_NAME) is None
|
||||
or infos.get(CONF_TEMP_SENSOR) is None
|
||||
or infos.get(CONF_CYCLE_MIN) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_MAIN_CENTRAL_CONFIG, False) is False
|
||||
and infos.get(CONF_EXTERNAL_TEMP_SENSOR) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_SWITCH
|
||||
and infos.get(CONF_HEATER, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_CLIMATE
|
||||
and infos.get(CONF_CLIMATE, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_THERMOSTAT_TYPE) == CONF_THERMOSTAT_VALVE
|
||||
and infos.get(CONF_VALVE, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_MOTION_FEATURE, False) is True
|
||||
and infos.get(CONF_MOTION_SENSOR, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_POWER_FEATURE, False) is True
|
||||
and infos.get(CONF_USE_POWER_CENTRAL_CONFIG, False) is False
|
||||
and (
|
||||
infos.get(CONF_POWER_SENSOR, None) is None
|
||||
or infos.get(CONF_MAX_POWER_SENSOR, None) is None
|
||||
)
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_PRESENCE_FEATURE, False) is True
|
||||
and infos.get(CONF_USE_PRESENCE_CENTRAL_CONFIG, False) is False
|
||||
and infos.get(CONF_PRESENCE_SENSOR, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if (
|
||||
infos.get(CONF_USE_ADVANCED_CENTRAL_CONFIG, False) is False
|
||||
and infos.get(CONF_MINIMAL_ACTIVATION_DELAY, -1) == -1
|
||||
):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def merge_user_input(self, data_schema: vol.Schema, user_input: dict):
|
||||
"""For each schema entry not in user_input, set or remove values in infos"""
|
||||
self._infos.update(user_input)
|
||||
@@ -239,11 +341,15 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
errors[str(err)] = "no_central_config"
|
||||
except ServiceConfigurationError as err:
|
||||
errors[str(err)] = "service_configuration_format"
|
||||
except ConfigurationNotCompleteError as err:
|
||||
errors["base"] = "configuration_not_complete"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
self.merge_user_input(data_schema, user_input)
|
||||
# Add default values for central config flags
|
||||
self._init_central_config_flags(self._infos)
|
||||
_LOGGER.debug("_info is now: %s", self._infos)
|
||||
return await next_step_function()
|
||||
|
||||
@@ -264,30 +370,92 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_user user_input=%s", user_input)
|
||||
|
||||
return await self.generic_step(
|
||||
"user", STEP_USER_DATA_SCHEMA, user_input, self.async_step_main
|
||||
"user", STEP_USER_DATA_SCHEMA, user_input, self.async_step_menu
|
||||
)
|
||||
|
||||
async def async_step_configuration_not_complete(
|
||||
self, user_input: dict | None = None
|
||||
) -> FlowResult:
|
||||
"""A fake step to handle the incomplete configuration flow"""
|
||||
return await self.async_step_menu(user_input)
|
||||
|
||||
async def async_step_menu(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_menu user_input=%s", user_input)
|
||||
|
||||
is_central_config = (
|
||||
self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
)
|
||||
|
||||
menu_options = ["main", "features"]
|
||||
if not is_central_config:
|
||||
menu_options.append("type")
|
||||
|
||||
if (
|
||||
self._infos.get(CONF_PROP_FUNCTION) == PROPORTIONAL_FUNCTION_TPI
|
||||
or is_central_config
|
||||
):
|
||||
menu_options.append("tpi")
|
||||
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] in [
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_VALVE,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
]:
|
||||
menu_options.append("presets")
|
||||
|
||||
if (
|
||||
is_central_config
|
||||
and self._infos.get(CONF_USE_CENTRAL_BOILER_FEATURE) is True
|
||||
):
|
||||
menu_options.append("central_boiler")
|
||||
|
||||
if self._infos[CONF_USE_WINDOW_FEATURE] is True:
|
||||
menu_options.append("window")
|
||||
|
||||
if self._infos[CONF_USE_MOTION_FEATURE] is True:
|
||||
menu_options.append("motion")
|
||||
|
||||
if self._infos[CONF_USE_POWER_FEATURE] is True:
|
||||
menu_options.append("power")
|
||||
|
||||
if self._infos[CONF_USE_PRESENCE_FEATURE] is True:
|
||||
menu_options.append("presence")
|
||||
|
||||
menu_options.append("advanced")
|
||||
|
||||
if self.check_config_complete(self._infos):
|
||||
menu_options.append("finalize")
|
||||
else:
|
||||
_LOGGER.info("The configuration is not terminated")
|
||||
menu_options.append("configuration_not_complete")
|
||||
|
||||
return self.async_show_menu(
|
||||
step_id="menu",
|
||||
menu_options=menu_options,
|
||||
description_placeholders=self._placeholders,
|
||||
)
|
||||
|
||||
async def async_step_main(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_main user_input=%s", user_input)
|
||||
|
||||
schema = STEP_MAIN_DATA_SCHEMA
|
||||
next_step = self.async_step_type
|
||||
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
self._infos[CONF_NAME] = CENTRAL_CONFIG_NAME
|
||||
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
|
||||
if user_input and user_input.get(CONF_ADD_CENTRAL_BOILER_CONTROL) is True:
|
||||
next_step = self.async_step_central_boiler
|
||||
else:
|
||||
next_step = self.async_step_tpi
|
||||
elif user_input and user_input.get(CONF_USE_MAIN_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_main
|
||||
else:
|
||||
schema = STEP_MAIN_DATA_SCHEMA
|
||||
# If we come from async_step_spec_main
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_main":
|
||||
next_step = self.async_step_type
|
||||
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_MAIN_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if user_input and self._infos.get(COMES_FROM) == "async_step_spec_main":
|
||||
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_main
|
||||
|
||||
return await self.generic_step("main", schema, user_input, next_step)
|
||||
|
||||
@@ -299,7 +467,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
schema = STEP_CENTRAL_MAIN_DATA_SCHEMA
|
||||
else:
|
||||
schema = STEP_CENTRAL_SPEC_MAIN_DATA_SCHEMA
|
||||
next_step = self.async_step_type
|
||||
next_step = self.async_step_menu
|
||||
|
||||
self._infos[COMES_FROM] = "async_step_spec_main"
|
||||
|
||||
@@ -315,7 +483,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
)
|
||||
|
||||
schema = STEP_CENTRAL_BOILER_SCHEMA
|
||||
next_step = self.async_step_tpi
|
||||
next_step = self.async_step_menu
|
||||
|
||||
return await self.generic_step("central_boiler", schema, user_input, next_step)
|
||||
|
||||
@@ -325,36 +493,54 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_SWITCH:
|
||||
return await self.generic_step(
|
||||
"type", STEP_THERMOSTAT_SWITCH, user_input, self.async_step_tpi
|
||||
"type", STEP_THERMOSTAT_SWITCH, user_input, self.async_step_menu
|
||||
)
|
||||
elif self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_VALVE:
|
||||
return await self.generic_step(
|
||||
"type", STEP_THERMOSTAT_VALVE, user_input, self.async_step_tpi
|
||||
"type", STEP_THERMOSTAT_VALVE, user_input, self.async_step_menu
|
||||
)
|
||||
else:
|
||||
return await self.generic_step(
|
||||
"type",
|
||||
STEP_THERMOSTAT_CLIMATE,
|
||||
user_input,
|
||||
self.async_step_presets,
|
||||
self.async_step_menu,
|
||||
)
|
||||
|
||||
async def async_step_features(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the Type flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_features user_input=%s", user_input)
|
||||
|
||||
return await self.generic_step(
|
||||
"features",
|
||||
(
|
||||
STEP_CENTRAL_FEATURES_DATA_SCHEMA
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
else STEP_FEATURES_DATA_SCHEMA
|
||||
),
|
||||
user_input,
|
||||
self.async_step_menu,
|
||||
)
|
||||
|
||||
async def async_step_tpi(self, user_input: dict | None = None) -> FlowResult:
|
||||
"""Handle the TPI flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_tpi user_input=%s", user_input)
|
||||
|
||||
schema = STEP_TPI_DATA_SCHEMA
|
||||
next_step = (
|
||||
self.async_step_spec_tpi
|
||||
if user_input and user_input.get(CONF_USE_TPI_CENTRAL_CONFIG) is False
|
||||
else self.async_step_presets
|
||||
)
|
||||
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_TPI_DATA_SCHEMA
|
||||
next_step = self.async_step_presets
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_tpi":
|
||||
schema = STEP_CENTRAL_TPI_DATA_SCHEMA
|
||||
else:
|
||||
schema = STEP_TPI_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_TPI_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if user_input and self._infos.get(COMES_FROM) == "async_step_spec_tpi":
|
||||
schema = STEP_CENTRAL_TPI_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_tpi
|
||||
|
||||
return await self.generic_step("tpi", schema, user_input, next_step)
|
||||
|
||||
@@ -364,7 +550,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
schema = STEP_CENTRAL_TPI_DATA_SCHEMA
|
||||
self._infos[COMES_FROM] = "async_step_spec_tpi"
|
||||
next_step = self.async_step_presets
|
||||
next_step = self.async_step_menu
|
||||
|
||||
return await self.generic_step("tpi", schema, user_input, next_step)
|
||||
|
||||
@@ -372,82 +558,41 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the presets flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_presets user_input=%s", user_input)
|
||||
|
||||
if self._infos.get(CONF_AC_MODE) is True:
|
||||
schema_ac_or_not = STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA
|
||||
else:
|
||||
schema_ac_or_not = STEP_CENTRAL_PRESETS_DATA_SCHEMA
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
next_step = self.async_step_menu # advanced
|
||||
schema = STEP_PRESETS_DATA_SCHEMA
|
||||
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
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
# In Central config -> display the next step immedialty
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA
|
||||
next_step = self.async_step_window
|
||||
# If comes from async_step_spec_presets
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_presets":
|
||||
schema = schema_ac_or_not
|
||||
elif user_input and user_input.get(CONF_USE_PRESETS_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_presets
|
||||
schema = STEP_PRESETS_DATA_SCHEMA
|
||||
# Call directly the next step, we have nothing to display here
|
||||
return await self.async_step_window() # = self.async_step_window
|
||||
|
||||
return await self.generic_step("presets", schema, user_input, next_step)
|
||||
|
||||
async def async_step_spec_presets(
|
||||
self, user_input: dict | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the specific presets flow steps"""
|
||||
_LOGGER.debug(
|
||||
"Into ConfigFlow.async_step_spec_presets user_input=%s", user_input
|
||||
)
|
||||
|
||||
if self._infos.get(CONF_AC_MODE) is True:
|
||||
schema = STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA
|
||||
else:
|
||||
schema = STEP_CENTRAL_PRESETS_DATA_SCHEMA
|
||||
|
||||
self._infos[COMES_FROM] = "async_step_spec_presets"
|
||||
|
||||
next_step = self.async_step_window
|
||||
|
||||
# This will return to async_step_main (to keep the "main" step)
|
||||
return await self.generic_step("presets", 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)
|
||||
|
||||
schema = STEP_WINDOW_DATA_SCHEMA
|
||||
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
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_WINDOW_DATA_SCHEMA
|
||||
next_step = self.async_step_motion
|
||||
# If comes from async_step_spec_window
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_window":
|
||||
# If we have a window sensor don't display the auto window parameters
|
||||
if self._infos.get(CONF_WINDOW_SENSOR) is not None:
|
||||
schema = STEP_CENTRAL_WINDOW_WO_AUTO_DATA_SCHEMA
|
||||
else:
|
||||
schema = STEP_CENTRAL_WINDOW_DATA_SCHEMA
|
||||
elif user_input and user_input.get(CONF_USE_WINDOW_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_window
|
||||
else:
|
||||
schema = STEP_WINDOW_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_WINDOW_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if (
|
||||
user_input
|
||||
and self._infos.get(COMES_FROM) == "async_step_spec_window"
|
||||
):
|
||||
if self._infos.get(CONF_WINDOW_SENSOR) is not None:
|
||||
schema = STEP_CENTRAL_WINDOW_WO_AUTO_DATA_SCHEMA
|
||||
else:
|
||||
schema = STEP_CENTRAL_WINDOW_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_window
|
||||
|
||||
return await self.generic_step("window", schema, user_input, next_step)
|
||||
|
||||
@@ -474,23 +619,24 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the window and motion sensor flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_motion user_input=%s", user_input)
|
||||
|
||||
schema = STEP_MOTION_DATA_SCHEMA
|
||||
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
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_MOTION_DATA_SCHEMA
|
||||
next_step = self.async_step_power
|
||||
# If comes from async_step_spec_motion
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_motion":
|
||||
schema = STEP_CENTRAL_MOTION_DATA_SCHEMA
|
||||
elif user_input and user_input.get(CONF_USE_MOTION_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_motion
|
||||
else:
|
||||
schema = STEP_MOTION_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_MOTION_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if (
|
||||
user_input
|
||||
and self._infos.get(COMES_FROM) == "async_step_spec_motion"
|
||||
):
|
||||
schema = STEP_CENTRAL_MOTION_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_motion
|
||||
|
||||
return await self.generic_step("motion", schema, user_input, next_step)
|
||||
|
||||
@@ -506,7 +652,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
self._infos[COMES_FROM] = "async_step_spec_motion"
|
||||
|
||||
next_step = self.async_step_power
|
||||
next_step = self.async_step_menu
|
||||
|
||||
# This will return to async_step_main (to keep the "main" step)
|
||||
return await self.generic_step("motion", schema, user_input, next_step)
|
||||
@@ -515,21 +661,24 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the power management flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_power user_input=%s", user_input)
|
||||
|
||||
schema = STEP_POWER_DATA_SCHEMA
|
||||
next_step = self.async_step_advanced
|
||||
|
||||
if self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
next_step = self.async_step_presence
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_POWER_DATA_SCHEMA
|
||||
next_step = self.async_step_presence
|
||||
# If comes from async_step_spec_motion
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_power":
|
||||
schema = STEP_CENTRAL_POWER_DATA_SCHEMA
|
||||
elif user_input and user_input.get(CONF_USE_POWER_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_power
|
||||
else:
|
||||
schema = STEP_POWER_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_POWER_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if (
|
||||
user_input
|
||||
and self._infos.get(COMES_FROM) == "async_step_spec_power"
|
||||
):
|
||||
schema = STEP_CENTRAL_POWER_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_power
|
||||
|
||||
return await self.generic_step("power", schema, user_input, next_step)
|
||||
|
||||
@@ -541,7 +690,7 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
self._infos[COMES_FROM] = "async_step_spec_power"
|
||||
|
||||
next_step = self.async_step_presence
|
||||
next_step = self.async_step_menu
|
||||
|
||||
# This will return to async_step_power (to keep the "power" step)
|
||||
return await self.generic_step("power", schema, user_input, next_step)
|
||||
@@ -550,25 +699,31 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
"""Handle the presence management flow steps"""
|
||||
_LOGGER.debug("Into ConfigFlow.async_step_presence user_input=%s", user_input)
|
||||
|
||||
schema = STEP_PRESENCE_DATA_SCHEMA
|
||||
next_step = self.async_step_advanced
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_PRESENCE_DATA_SCHEMA
|
||||
next_step = self.async_step_advanced
|
||||
# If comes from async_step_spec_presence
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_presence":
|
||||
schema = STEP_CENTRAL_PRESENCE_DATA_SCHEMA
|
||||
elif user_input and user_input.get(CONF_USE_PRESENCE_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_presence
|
||||
else:
|
||||
schema = STEP_PRESENCE_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_PRESENCE_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if (
|
||||
user_input
|
||||
and self._infos.get(COMES_FROM) == "async_step_spec_presence"
|
||||
):
|
||||
schema = STEP_CENTRAL_PRESENCE_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_presence
|
||||
|
||||
return await self.generic_step("presence", schema, user_input, next_step)
|
||||
|
||||
async def async_step_spec_presence(
|
||||
self, user_input: dict | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the specific preseence flow steps"""
|
||||
"""Handle the specific power flow steps"""
|
||||
_LOGGER.debug(
|
||||
"Into ConfigFlow.async_step_spec_presence user_input=%s", user_input
|
||||
)
|
||||
@@ -577,26 +732,33 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
|
||||
self._infos[COMES_FROM] = "async_step_spec_presence"
|
||||
|
||||
next_step = self.async_step_advanced
|
||||
next_step = self.async_step_menu
|
||||
|
||||
# This will return to async_step_presence (to keep the "presence" step)
|
||||
# This will return to async_step_power (to keep the "power" step)
|
||||
return await self.generic_step("presence", schema, user_input, next_step)
|
||||
|
||||
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)
|
||||
|
||||
schema = STEP_ADVANCED_DATA_SCHEMA
|
||||
next_step = self.async_finalize
|
||||
|
||||
# In Central config -> display the presets_with_ac and goto windows
|
||||
next_step = self.async_step_menu
|
||||
if self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
schema = STEP_CENTRAL_ADVANCED_DATA_SCHEMA
|
||||
# If comes from async_step_spec_presence
|
||||
elif self._infos.get(COMES_FROM) == "async_step_spec_advanced":
|
||||
schema = STEP_CENTRAL_ADVANCED_DATA_SCHEMA
|
||||
elif user_input and user_input.get(CONF_USE_ADVANCED_CENTRAL_CONFIG) is False:
|
||||
next_step = self.async_step_spec_advanced
|
||||
else:
|
||||
schema = STEP_ADVANCED_DATA_SCHEMA
|
||||
|
||||
if (
|
||||
user_input
|
||||
and user_input.get(CONF_USE_ADVANCED_CENTRAL_CONFIG, False) is False
|
||||
):
|
||||
if (
|
||||
user_input
|
||||
and self._infos.get(COMES_FROM) == "async_step_spec_advanced"
|
||||
):
|
||||
schema = STEP_CENTRAL_ADVANCED_DATA_SCHEMA
|
||||
del self._infos[COMES_FROM]
|
||||
else:
|
||||
next_step = self.async_step_spec_advanced
|
||||
|
||||
return await self.generic_step("advanced", schema, user_input, next_step)
|
||||
|
||||
@@ -617,22 +779,12 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
# This will return to async_step_presence (to keep the "presence" step)
|
||||
return await self.generic_step("advanced", schema, user_input, next_step)
|
||||
|
||||
async def async_finalize(self):
|
||||
async def async_step_finalize(self, _):
|
||||
"""Should be implemented by Leaf classes"""
|
||||
raise HomeAssistantError(
|
||||
"async_finalize not implemented on VersatileThermostat sub-class"
|
||||
)
|
||||
|
||||
# Not used but can be useful in the future
|
||||
# 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
|
||||
@@ -650,7 +802,7 @@ class VersatileThermostatConfigFlow(
|
||||
"""Get options flow for this handler"""
|
||||
return VersatileThermostatOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_finalize(self):
|
||||
async def async_step_finalize(self, _):
|
||||
"""Finalization of the ConfigEntry creation"""
|
||||
_LOGGER.debug("ConfigFlow.async_finalize")
|
||||
# Removes temporary value
|
||||
@@ -685,155 +837,9 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
CONF_NAME: self._infos[CONF_NAME],
|
||||
}
|
||||
|
||||
return await self.async_step_main(user_input)
|
||||
return await self.async_step_menu(user_input)
|
||||
|
||||
# async def async_step_main(self, user_input: dict | None = None) -> FlowResult:
|
||||
# """Handle the flow steps"""
|
||||
# _LOGGER.debug(
|
||||
# "Into OptionsFlowHandler.async_step_user user_input=%s", user_input
|
||||
# )
|
||||
|
||||
# return await self.generic_step(
|
||||
# "user", 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", STEP_THERMOSTAT_SWITCH, user_input, self.async_step_tpi
|
||||
# )
|
||||
# elif self._infos[CONF_THERMOSTAT_TYPE] == CONF_THERMOSTAT_VALVE:
|
||||
# return await self.generic_step(
|
||||
# "type", STEP_THERMOSTAT_VALVE, user_input, self.async_step_tpi
|
||||
# )
|
||||
# else:
|
||||
# return await self.generic_step(
|
||||
# "type",
|
||||
# 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(
|
||||
# "Into OptionsFlowHandler.async_step_tpi user_input=%s", user_input
|
||||
# )
|
||||
|
||||
# return await self.generic_step(
|
||||
# "tpi", STEP_TPI_DATA_SCHEMA, user_input, self.async_step_presets
|
||||
# )
|
||||
|
||||
# async def async_step_presets(self, user_input: dict | None = None) -> FlowResult:
|
||||
# """Handle the presets flow steps"""
|
||||
# _LOGGER.debug(
|
||||
# "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
|
||||
|
||||
# if self._infos.get(CONF_AC_MODE) is True:
|
||||
# schema = STEP_PRESETS_WITH_AC_DATA_SCHEMA
|
||||
# else:
|
||||
# schema = STEP_PRESETS_DATA_SCHEMA
|
||||
|
||||
# return await self.generic_step("presets", 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 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", 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 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", 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 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",
|
||||
# STEP_POWER_DATA_SCHEMA,
|
||||
# user_input,
|
||||
# next_step,
|
||||
# )
|
||||
|
||||
# async def async_step_presence(self, user_input: dict | None = None) -> FlowResult:
|
||||
# """Handle the presence management flow steps"""
|
||||
# _LOGGER.debug(
|
||||
# "Into OptionsFlowHandler.async_step_presence user_input=%s", user_input
|
||||
# )
|
||||
|
||||
# if self._infos.get(CONF_AC_MODE) is True:
|
||||
# schema = STEP_PRESENCE_WITH_AC_DATA_SCHEMA
|
||||
# else:
|
||||
# schema = STEP_PRESENCE_DATA_SCHEMA
|
||||
|
||||
# return await self.generic_step(
|
||||
# "presence",
|
||||
# schema,
|
||||
# user_input,
|
||||
# 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",
|
||||
# STEP_ADVANCED_DATA_SCHEMA,
|
||||
# user_input,
|
||||
# self.async_end,
|
||||
# )
|
||||
|
||||
async def async_finalize(self):
|
||||
async def async_step_finalize(self, _):
|
||||
"""Finalization of the ConfigEntry creation"""
|
||||
if not self._infos[CONF_USE_WINDOW_FEATURE]:
|
||||
self._infos[CONF_USE_WINDOW_CENTRAL_CONFIG] = False
|
||||
|
||||
@@ -44,13 +44,28 @@ STEP_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
),
|
||||
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
|
||||
vol.Optional(CONF_DEVICE_POWER, default="1"): vol.Coerce(float),
|
||||
vol.Optional(CONF_USE_CENTRAL_MODE, default=True): cv.boolean,
|
||||
vol.Required(CONF_USE_MAIN_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
vol.Optional(CONF_USE_CENTRAL_MODE, default=True): cv.boolean,
|
||||
vol.Required(CONF_USED_BY_CENTRAL_BOILER, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_FEATURES_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_PRESENCE_FEATURE, default=False): cv.boolean,
|
||||
vol.Required(CONF_USED_BY_CENTRAL_BOILER, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_FEATURES_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_PRESENCE_FEATURE, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USE_CENTRAL_BOILER_FEATURE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -62,7 +77,6 @@ STEP_CENTRAL_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
vol.Required(CONF_TEMP_MIN, default=7): vol.Coerce(float),
|
||||
vol.Required(CONF_TEMP_MAX, default=35): vol.Coerce(float),
|
||||
vol.Required(CONF_STEP_TEMPERATURE, default=0.1): vol.Coerce(float),
|
||||
vol.Required(CONF_ADD_CENTRAL_BOILER_CONTROL, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -192,18 +206,6 @@ STEP_PRESETS_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESETS_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{vol.Optional(v, default=0): vol.Coerce(float) for (k, v) in CONF_PRESETS.items()}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESETS_WITH_AC_DATA_SCHEMA = (
|
||||
vol.Schema( # pylint: disable=invalid-name # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(v, default=0): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_WITH_AC.items()
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
STEP_WINDOW_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
@@ -251,7 +253,7 @@ STEP_CENTRAL_WINDOW_WO_AUTO_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid
|
||||
|
||||
STEP_MOTION_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_MOTION_SENSOR): selector.EntitySelector(
|
||||
vol.Required(CONF_MOTION_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[BINARY_SENSOR_DOMAIN, INPUT_BOOLEAN_DOMAIN]
|
||||
),
|
||||
@@ -283,10 +285,10 @@ STEP_CENTRAL_MOTION_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
|
||||
STEP_CENTRAL_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_POWER_SENSOR): selector.EntitySelector(
|
||||
vol.Required(CONF_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_MAX_POWER_SENSOR): selector.EntitySelector(
|
||||
vol.Required(CONF_MAX_POWER_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]),
|
||||
),
|
||||
vol.Optional(CONF_PRESET_POWER, default="13"): vol.Coerce(float),
|
||||
@@ -301,19 +303,7 @@ STEP_POWER_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
|
||||
STEP_CENTRAL_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(v, default=17): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_AWAY.items()
|
||||
}
|
||||
)
|
||||
|
||||
STEP_CENTRAL_PRESENCE_WITH_AC_DATA_SCHEMA = { # pylint: disable=invalid-name
|
||||
vol.Optional(v, default=17): vol.Coerce(float)
|
||||
for (k, v) in CONF_PRESETS_AWAY_WITH_AC.items()
|
||||
}
|
||||
|
||||
STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Optional(CONF_PRESENCE_SENSOR): selector.EntitySelector(
|
||||
vol.Required(CONF_PRESENCE_SENSOR): selector.EntitySelector(
|
||||
selector.EntitySelectorConfig(
|
||||
domain=[
|
||||
PERSON_DOMAIN,
|
||||
@@ -321,7 +311,12 @@ STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
INPUT_BOOLEAN_DOMAIN,
|
||||
]
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
|
||||
{
|
||||
vol.Required(CONF_USE_PRESENCE_CENTRAL_CONFIG, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -22,6 +22,10 @@ from .prop_algorithm import (
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_VERSION = 1
|
||||
CONFIG_MINOR_VERSION = 2
|
||||
|
||||
PRESET_TEMP_SUFFIX = "_temp"
|
||||
PRESET_AC_SUFFIX = "_ac"
|
||||
PRESET_ECO_AC = PRESET_ECO + PRESET_AC_SUFFIX
|
||||
PRESET_COMFORT_AC = PRESET_COMFORT + PRESET_AC_SUFFIX
|
||||
@@ -39,11 +43,13 @@ HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
|
||||
|
||||
DOMAIN = "versatile_thermostat"
|
||||
|
||||
# The order is important.
|
||||
PLATFORMS: list[Platform] = [
|
||||
Platform.NUMBER,
|
||||
Platform.SELECT,
|
||||
Platform.CLIMATE,
|
||||
Platform.SENSOR,
|
||||
# Number should be after CLIMATE
|
||||
Platform.NUMBER,
|
||||
Platform.BINARY_SENSOR,
|
||||
]
|
||||
|
||||
@@ -89,6 +95,7 @@ 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_USE_CENTRAL_BOILER_FEATURE = "use_central_boiler_feature"
|
||||
CONF_AC_MODE = "ac_mode"
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD = "window_auto_open_threshold"
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD = "window_auto_close_threshold"
|
||||
@@ -131,7 +138,6 @@ CONF_USE_ADVANCED_CENTRAL_CONFIG = "use_advanced_central_config"
|
||||
|
||||
CONF_USE_CENTRAL_MODE = "use_central_mode"
|
||||
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL = "add_central_boiler_control"
|
||||
CONF_CENTRAL_BOILER_ACTIVATION_SRV = "central_boiler_activation_service"
|
||||
CONF_CENTRAL_BOILER_DEACTIVATION_SRV = "central_boiler_deactivation_service"
|
||||
|
||||
@@ -146,7 +152,7 @@ DEFAULT_SHORT_EMA_PARAMS = {
|
||||
}
|
||||
|
||||
CONF_PRESETS = {
|
||||
p: f"{p}_temp"
|
||||
p: f"{p}{PRESET_TEMP_SUFFIX}"
|
||||
for p in (
|
||||
PRESET_FROST_PROTECTION,
|
||||
PRESET_ECO,
|
||||
@@ -156,7 +162,7 @@ CONF_PRESETS = {
|
||||
}
|
||||
|
||||
CONF_PRESETS_WITH_AC = {
|
||||
p: f"{p}_temp"
|
||||
p: f"{p}{PRESET_TEMP_SUFFIX}"
|
||||
for p in (
|
||||
PRESET_FROST_PROTECTION,
|
||||
PRESET_ECO,
|
||||
@@ -172,7 +178,7 @@ CONF_PRESETS_WITH_AC = {
|
||||
PRESET_AWAY_SUFFIX = "_away"
|
||||
|
||||
CONF_PRESETS_AWAY = {
|
||||
p: f"{p}_temp"
|
||||
p: f"{p}{PRESET_TEMP_SUFFIX}"
|
||||
for p in (
|
||||
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX,
|
||||
PRESET_ECO + PRESET_AWAY_SUFFIX,
|
||||
@@ -182,7 +188,7 @@ CONF_PRESETS_AWAY = {
|
||||
}
|
||||
|
||||
CONF_PRESETS_AWAY_WITH_AC = {
|
||||
p: f"{p}_temp"
|
||||
p: f"{p}{PRESET_TEMP_SUFFIX}"
|
||||
for p in (
|
||||
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX,
|
||||
PRESET_ECO + PRESET_AWAY_SUFFIX,
|
||||
@@ -250,6 +256,7 @@ ALL_CONF = (
|
||||
CONF_USE_MOTION_FEATURE,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_POWER_FEATURE,
|
||||
CONF_USE_CENTRAL_BOILER_FEATURE,
|
||||
CONF_AC_MODE,
|
||||
CONF_VALVE,
|
||||
CONF_VALVE_2,
|
||||
@@ -270,7 +277,6 @@ ALL_CONF = (
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG,
|
||||
CONF_USE_CENTRAL_MODE,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL,
|
||||
CONF_USED_BY_CENTRAL_BOILER,
|
||||
CONF_CENTRAL_BOILER_ACTIVATION_SRV,
|
||||
CONF_CENTRAL_BOILER_DEACTIVATION_SRV,
|
||||
@@ -361,7 +367,9 @@ CENTRAL_MODES = [
|
||||
class RegulationParamSlow:
|
||||
"""Light parameters for slow latency regulation"""
|
||||
|
||||
kp: float = 0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
|
||||
kp: float = (
|
||||
0.2 # 20% of the current internal regulation offset are caused by the current difference of target temperature and room temperature
|
||||
)
|
||||
ki: float = (
|
||||
0.8 / 288.0
|
||||
) # 80% of the current internal regulation offset are caused by the average offset of the past 24 hours
|
||||
@@ -369,7 +377,9 @@ class RegulationParamSlow:
|
||||
1.0 / 25.0
|
||||
) # this will add 1°C to the offset when it's 25°C colder outdoor than indoor
|
||||
offset_max: float = 2.0 # limit to a final offset of -2°C to +2°C
|
||||
stabilization_threshold: float = 0.0 # this needs to be disabled as otherwise the long term accumulated error will always be reset when the temp briefly crosses from/to below/above the target
|
||||
stabilization_threshold: float = (
|
||||
0.0 # this needs to be disabled as otherwise the long term accumulated error will always be reset when the temp briefly crosses from/to below/above the target
|
||||
)
|
||||
accumulated_error_threshold: float = (
|
||||
2.0 * 288
|
||||
) # this allows up to 2°C long term offset in both directions
|
||||
@@ -461,6 +471,10 @@ class ServiceConfigurationError(HomeAssistantError):
|
||||
"""Error in the service configuration to control the central boiler"""
|
||||
|
||||
|
||||
class ConfigurationNotCompleteError(HomeAssistantError):
|
||||
"""Error the configuration is not complete"""
|
||||
|
||||
|
||||
class overrides: # pylint: disable=invalid-name
|
||||
"""An annotation to inform overrides"""
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"quality_scale": "silver",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"version": "5.4.1",
|
||||
"version": "6.0.0",
|
||||
"zeroconf": []
|
||||
}
|
||||
@@ -6,24 +6,75 @@ import logging
|
||||
# from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, CoreState # , callback
|
||||
|
||||
from homeassistant.components.number import NumberEntity, NumberMode
|
||||
from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberMode,
|
||||
NumberDeviceClass,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.climate import (
|
||||
PRESET_BOOST,
|
||||
PRESET_COMFORT,
|
||||
PRESET_ECO,
|
||||
)
|
||||
from homeassistant.components.sensor import UnitOfTemperature
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
from .commons import VersatileThermostatBaseEntity
|
||||
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
DEVICE_MANUFACTURER,
|
||||
CONF_NAME,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL,
|
||||
CONF_TEMP_MIN,
|
||||
CONF_TEMP_MAX,
|
||||
CONF_STEP_TEMPERATURE,
|
||||
CONF_AC_MODE,
|
||||
PRESET_FROST_PROTECTION,
|
||||
PRESET_ECO_AC,
|
||||
PRESET_COMFORT_AC,
|
||||
PRESET_BOOST_AC,
|
||||
PRESET_AWAY_SUFFIX,
|
||||
PRESET_TEMP_SUFFIX,
|
||||
CONF_PRESETS_VALUES,
|
||||
CONF_PRESETS_WITH_AC_VALUES,
|
||||
CONF_PRESETS_AWAY_VALUES,
|
||||
CONF_PRESETS_AWAY_WITH_AC_VALUES,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
overrides,
|
||||
)
|
||||
|
||||
PRESET_ICON_MAPPING = {
|
||||
PRESET_FROST_PROTECTION + PRESET_TEMP_SUFFIX: "mdi:snowflake-thermometer",
|
||||
PRESET_ECO + PRESET_TEMP_SUFFIX: "mdi:leaf",
|
||||
PRESET_COMFORT + PRESET_TEMP_SUFFIX: "mdi:sofa",
|
||||
PRESET_BOOST + PRESET_TEMP_SUFFIX: "mdi:rocket-launch",
|
||||
PRESET_ECO_AC + PRESET_TEMP_SUFFIX: "mdi:leaf-circle-outline",
|
||||
PRESET_COMFORT_AC + PRESET_TEMP_SUFFIX: "mdi:sofa-outline",
|
||||
PRESET_BOOST_AC + PRESET_TEMP_SUFFIX: "mdi:rocket-launch-outline",
|
||||
PRESET_FROST_PROTECTION
|
||||
+ PRESET_AWAY_SUFFIX
|
||||
+ PRESET_TEMP_SUFFIX: "mdi:snowflake-thermometer",
|
||||
PRESET_ECO + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: "mdi:leaf",
|
||||
PRESET_COMFORT + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: "mdi:sofa",
|
||||
PRESET_BOOST + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: "mdi:rocket-launch",
|
||||
PRESET_ECO_AC + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: "mdi:leaf-circle-outline",
|
||||
PRESET_COMFORT_AC + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: "mdi:sofa-outline",
|
||||
PRESET_BOOST_AC
|
||||
+ PRESET_AWAY_SUFFIX
|
||||
+ PRESET_TEMP_SUFFIX: "mdi:rocket-launch-outline",
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -40,19 +91,82 @@ async def async_setup_entry(
|
||||
unique_id = entry.entry_id
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
is_central_boiler = entry.data.get(CONF_ADD_CENTRAL_BOILER_CONTROL)
|
||||
# is_central_boiler = entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE)
|
||||
|
||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG or not is_central_boiler:
|
||||
return
|
||||
entities = []
|
||||
|
||||
entities = [
|
||||
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data),
|
||||
]
|
||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
# Creates non central temperature entities
|
||||
if not entry.data.get(CONF_USE_PRESETS_CENTRAL_CONFIG, False):
|
||||
for preset in CONF_PRESETS_VALUES:
|
||||
entities.append(
|
||||
TemperatureNumber(
|
||||
hass, unique_id, name, preset, False, False, entry.data
|
||||
)
|
||||
)
|
||||
if entry.data.get(CONF_AC_MODE, False):
|
||||
for preset in CONF_PRESETS_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
TemperatureNumber(
|
||||
hass, unique_id, name, preset, True, False, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
if entry.data.get(
|
||||
CONF_USE_PRESENCE_FEATURE, False
|
||||
) is True and not entry.data.get(CONF_USE_PRESENCE_CENTRAL_CONFIG, False):
|
||||
for preset in CONF_PRESETS_AWAY_VALUES:
|
||||
entities.append(
|
||||
TemperatureNumber(
|
||||
hass, unique_id, name, preset, False, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
if entry.data.get(CONF_AC_MODE, False):
|
||||
for preset in CONF_PRESETS_AWAY_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
TemperatureNumber(
|
||||
hass, unique_id, name, preset, True, True, entry.data
|
||||
)
|
||||
)
|
||||
# For central config only
|
||||
else:
|
||||
entities.append(
|
||||
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data)
|
||||
)
|
||||
for preset in CONF_PRESETS_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, name, preset, False, False, entry.data
|
||||
)
|
||||
)
|
||||
for preset in CONF_PRESETS_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, name, preset, True, False, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
for preset in CONF_PRESETS_AWAY_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, name, preset, False, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
for preset in CONF_PRESETS_AWAY_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, name, preset, True, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class ActivateBoilerThresholdNumber(NumberEntity, RestoreEntity):
|
||||
class ActivateBoilerThresholdNumber(
|
||||
NumberEntity, RestoreEntity
|
||||
): # pylint: disable=abstract-method
|
||||
"""Representation of the threshold of the number of VTherm
|
||||
which should be active to activate the boiler"""
|
||||
|
||||
@@ -115,3 +229,239 @@ class ActivateBoilerThresholdNumber(NumberEntity, RestoreEntity):
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
|
||||
class CentralConfigTemperatureNumber(
|
||||
NumberEntity, RestoreEntity
|
||||
): # pylint: disable=abstract-method
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name,
|
||||
preset_name,
|
||||
is_ac,
|
||||
is_away,
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the temperature with entry_infos if available. Else
|
||||
the restoration will do the trick."""
|
||||
|
||||
self._config_id = unique_id
|
||||
self._device_name = name
|
||||
# self._attr_name = name
|
||||
|
||||
self._attr_translation_key = preset_name
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.{slugify(name)}_preset_{preset_name}"
|
||||
self._attr_unique_id = f"central_configuration_preset_{preset_name}"
|
||||
self._attr_device_class = NumberDeviceClass.TEMPERATURE
|
||||
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
|
||||
self._attr_native_step = entry_infos.get(CONF_STEP_TEMPERATURE, 0.5)
|
||||
self._attr_native_min_value = entry_infos.get(CONF_TEMP_MIN)
|
||||
self._attr_native_max_value = entry_infos.get(CONF_TEMP_MAX)
|
||||
|
||||
# Initialize the values if included into the entry_infos. This will do
|
||||
# the temperature migration. Else the temperature will be restored from
|
||||
# previous value
|
||||
# TODO remove this after the next major release and just keep the init min/max
|
||||
temp = None
|
||||
if (temp := entry_infos.get(preset_name, None)) is not None:
|
||||
self._attr_value = self._attr_native_value = temp
|
||||
else:
|
||||
if entry_infos.get(CONF_AC_MODE) is True:
|
||||
self._attr_native_value = self._attr_native_max_value
|
||||
else:
|
||||
self._attr_native_value = self._attr_native_min_value
|
||||
|
||||
self._attr_mode = NumberMode.BOX
|
||||
self._preset_name = preset_name
|
||||
self._is_away = is_away
|
||||
self._is_ac = is_ac
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
return PRESET_ICON_MAPPING[self._preset_name]
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info."""
|
||||
return DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, self._config_id)},
|
||||
name=self._device_name,
|
||||
manufacturer=DEVICE_MANUFACTURER,
|
||||
model=DOMAIN,
|
||||
)
|
||||
|
||||
@overrides
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# register the temp entity for this device and preset
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
|
||||
api.register_temperature_number(self._config_id, self._preset_name, self)
|
||||
|
||||
# Restore value from previous one if exists
|
||||
old_state: CoreState = await self.async_get_last_state()
|
||||
_LOGGER.debug(
|
||||
"%s - Calling async_added_to_hass old_state is %s", self, old_state
|
||||
)
|
||||
try:
|
||||
if old_state is not None and ((value := float(old_state.state)) > 0):
|
||||
self._attr_value = self._attr_native_value = value
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@overrides
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""The value have change from the Number Entity in UI"""
|
||||
float_value = float(value)
|
||||
old_value = float(self._attr_native_value)
|
||||
if float_value == old_value:
|
||||
return
|
||||
|
||||
self._attr_value = self._attr_native_value = float_value
|
||||
|
||||
# persist the value
|
||||
self.async_write_ha_state()
|
||||
|
||||
# We have to reload all VTherm for which uses the central configuration
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
|
||||
# Update the VTherms which have temperature in central config
|
||||
self.hass.create_task(api.init_vtherm_links(only_use_central=True))
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""The unit of measurement"""
|
||||
# TODO Kelvin ? It seems not because all internal values are stored in
|
||||
# ° Celsius but only the render in front can be in °K depending on the
|
||||
# user configuration.
|
||||
return UnitOfTemperature.CELSIUS
|
||||
|
||||
|
||||
class TemperatureNumber( # pylint: disable=abstract-method
|
||||
VersatileThermostatBaseEntity, NumberEntity, RestoreEntity
|
||||
):
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name,
|
||||
preset_name,
|
||||
is_ac,
|
||||
is_away,
|
||||
entry_infos,
|
||||
) -> None:
|
||||
"""Initialize the temperature with entry_infos if available. Else
|
||||
the restoration will do the trick."""
|
||||
super().__init__(hass, unique_id, name)
|
||||
|
||||
self._attr_translation_key = preset_name
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.{slugify(name)}_preset_{preset_name}"
|
||||
|
||||
self._attr_unique_id = f"{self._device_name}_preset_{preset_name}"
|
||||
self._attr_device_class = NumberDeviceClass.TEMPERATURE
|
||||
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
|
||||
self._attr_native_step = entry_infos.get(CONF_STEP_TEMPERATURE, 0.5)
|
||||
self._attr_native_min_value = entry_infos.get(CONF_TEMP_MIN)
|
||||
self._attr_native_max_value = entry_infos.get(CONF_TEMP_MAX)
|
||||
|
||||
# Initialize the values if included into the entry_infos. This will do
|
||||
# the temperature migration.
|
||||
temp = None
|
||||
if (temp := entry_infos.get(preset_name, None)) is not None:
|
||||
self._attr_value = self._attr_native_value = temp
|
||||
else:
|
||||
if entry_infos.get(CONF_AC_MODE) is True:
|
||||
self._attr_native_value = self._attr_native_max_value
|
||||
else:
|
||||
self._attr_native_value = self._attr_native_min_value
|
||||
|
||||
self._attr_mode = NumberMode.BOX
|
||||
self._preset_name = preset_name
|
||||
self._canonical_preset_name = preset_name.replace(
|
||||
PRESET_TEMP_SUFFIX, ""
|
||||
).replace(PRESET_AWAY_SUFFIX, "")
|
||||
self._is_away = is_away
|
||||
self._is_ac = is_ac
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
return PRESET_ICON_MAPPING[self._preset_name]
|
||||
|
||||
@overrides
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
|
||||
# register the temp entity for this device and preset
|
||||
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
|
||||
api.register_temperature_number(self._config_id, self._preset_name, self)
|
||||
|
||||
old_state: CoreState = await self.async_get_last_state()
|
||||
_LOGGER.debug(
|
||||
"%s - Calling async_added_to_hass old_state is %s", self, old_state
|
||||
)
|
||||
try:
|
||||
if old_state is not None and ((value := float(old_state.state)) > 0):
|
||||
self._attr_value = self._attr_native_value = value
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@overrides
|
||||
def my_climate_is_initialized(self):
|
||||
"""Called when the associated climate is initialized"""
|
||||
self._attr_native_step = self.my_climate.target_temperature_step
|
||||
self._attr_native_min_value = self.my_climate.min_temp
|
||||
self._attr_native_max_value = self.my_climate.max_temp
|
||||
return
|
||||
|
||||
@overrides
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Change the value"""
|
||||
|
||||
if self.my_climate is None:
|
||||
_LOGGER.warning(
|
||||
"%s - cannot change temperature because VTherm is not initialized", self
|
||||
)
|
||||
return
|
||||
|
||||
float_value = float(value)
|
||||
old_value = float(self._attr_native_value)
|
||||
|
||||
if float_value == old_value:
|
||||
return
|
||||
|
||||
self._attr_value = self._attr_native_value = float_value
|
||||
self.async_write_ha_state()
|
||||
|
||||
# Update the VTherm temp
|
||||
self.hass.create_task(
|
||||
self.my_climate.service_set_preset_temperature(
|
||||
self._canonical_preset_name,
|
||||
self._attr_native_value if not self._is_away else None,
|
||||
self._attr_native_value if self._is_away else None,
|
||||
)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""The unit of measurement"""
|
||||
if not self.my_climate:
|
||||
return UnitOfTemperature.CELSIUS
|
||||
return self.my_climate.temperature_unit
|
||||
|
||||
@@ -49,7 +49,7 @@ from .const import (
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL,
|
||||
CONF_USE_CENTRAL_BOILER_FEATURE,
|
||||
overrides,
|
||||
)
|
||||
|
||||
@@ -75,7 +75,7 @@ async def async_setup_entry(
|
||||
entities = None
|
||||
|
||||
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
if entry.data.get(CONF_ADD_CENTRAL_BOILER_CONTROL):
|
||||
if entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE):
|
||||
entities = [
|
||||
NbActiveDeviceForBoilerSensor(hass, unique_id, name, entry.data)
|
||||
]
|
||||
|
||||
@@ -12,13 +12,32 @@
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Configure your thermostat. You will be able to finalize the configuration when all needed parameters are valued.",
|
||||
"menu_options": {
|
||||
"main": "Main attributes",
|
||||
"central_boiler": "Central boiler",
|
||||
"type": "Underlyings",
|
||||
"tpi": "TPI parameters",
|
||||
"features": "Features",
|
||||
"presets": "Presets",
|
||||
"window": "Window detection",
|
||||
"motion": "Motion detection",
|
||||
"power": "Power management",
|
||||
"presence": "Presence detection",
|
||||
"advanced": "Advanced parameters",
|
||||
"finalize": "All done",
|
||||
"configuration_not_complete": "Configuration not complete"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"temperature_sensor_entity_id": "Room temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
@@ -26,18 +45,24 @@
|
||||
"step_temperature": "Temperature step",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config). Check to enable the control of the VTherm with the select central_mode entities.",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration. Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"add_central_boiler_control": "Add a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the next configuration page",
|
||||
"use_main_central_config": "Use additional central main configuration. Check to use the central main configuration (outdoor temperature, min, max, step, ...).",
|
||||
"used_by_controls_central_boiler": "Used by central boiler. Check if this VTherm should have control on the central boiler"
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Features",
|
||||
"description": "Thermostat features",
|
||||
"data": {
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entities",
|
||||
"description": "Linked entities attributes",
|
||||
@@ -104,26 +129,9 @@
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
"comfort_temp": "Comfort preset",
|
||||
"boost_temp": "Boost preset",
|
||||
"frost_temp": "Frost protection preset",
|
||||
"eco_ac_temp": "Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
"comfort_temp": "Temperature in Comfort preset",
|
||||
"boost_temp": "Temperature in Boost preset",
|
||||
"frost_temp": "Temperature in Frost protection preset",
|
||||
"eco_ac_temp": "Temperature in Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -189,25 +197,10 @@
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco preset",
|
||||
"comfort_away_temp": "Comfort preset",
|
||||
"boost_away_temp": "Boost preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost pres et in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
"use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
"eco_away_temp": "Temperature in Eco preset when no presence",
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence",
|
||||
"frost_away_temp": "Temperature in Frost protection preset when no presence",
|
||||
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
|
||||
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
"presence_sensor_entity_id": "Presence sensor entity id"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -251,6 +244,25 @@
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Configure your thermostat. You will be able to finalize the configuration when all needed parameters are valued.",
|
||||
"menu_options": {
|
||||
"main": "Main attributes",
|
||||
"central_boiler": "Central boiler",
|
||||
"type": "Underlyings",
|
||||
"tpi": "TPI parameters",
|
||||
"features": "Features",
|
||||
"presets": "Presets",
|
||||
"window": "Window detection",
|
||||
"motion": "Motion detection",
|
||||
"power": "Power management",
|
||||
"presence": "Presence detection",
|
||||
"advanced": "Advanced parameters",
|
||||
"finalize": "All done",
|
||||
"configuration_not_complete": "Configuration not complete"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Main - {name}",
|
||||
"description": "Main mandatory attributes",
|
||||
@@ -265,18 +277,24 @@
|
||||
"step_temperature": "Temperature step",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config). Check to enable the control of the VTherm with the select central_mode entities.",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration. Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"add_central_boiler_control": "Add a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the next configuration page",
|
||||
"use_main_central_config": "Use additional central main configuration. Check to use the central main configuration (outdoor temperature, min, max, step, ...).",
|
||||
"used_by_controls_central_boiler": "Used by central boiler. Check if this VTherm should have control on the central boiler"
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Features - {name}",
|
||||
"description": "Thermostat features",
|
||||
"data": {
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entities - {name}",
|
||||
"description": "Linked entities attributes",
|
||||
@@ -343,26 +361,9 @@
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets - {name}",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
"comfort_temp": "Comfort preset",
|
||||
"boost_temp": "Boost preset",
|
||||
"frost_temp": "Frost protection preset",
|
||||
"eco_ac_temp": "Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
"comfort_temp": "Temperature in Comfort preset",
|
||||
"boost_temp": "Temperature in Boost preset",
|
||||
"frost_temp": "Temperature in Frost protection preset",
|
||||
"eco_ac_temp": "Temperature in Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -428,25 +429,10 @@
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco away preset",
|
||||
"comfort_away_temp": "Comfort away preset",
|
||||
"boost_away_temp": "Boost away preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco away preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort away preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost away preset in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
"use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
"eco_away_temp": "Temperature in Eco preset when no presence",
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence",
|
||||
"frost_away_temp": "Temperature in Frost protection preset when no presence",
|
||||
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
|
||||
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
"presence_sensor_entity_id": "Presence sensor entity id"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -537,6 +523,53 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"frost_temp": {
|
||||
"name": "Frost"
|
||||
},
|
||||
"eco_temp": {
|
||||
"name": "Eco"
|
||||
},
|
||||
"comfort_temp": {
|
||||
"name": "Comfort"
|
||||
},
|
||||
"boost_temp": {
|
||||
"name": "Boost"
|
||||
},
|
||||
"frost_ac_temp": {
|
||||
"name": "Frost ac"
|
||||
},
|
||||
"eco_ac_temp": {
|
||||
"name": "Eco ac"
|
||||
},
|
||||
"comfort_ac_temp": {
|
||||
"name": "Comfort ac"
|
||||
},
|
||||
"boost_ac_temp": {
|
||||
"name": "Boost ac"
|
||||
},
|
||||
"frost_away_temp": {
|
||||
"name": "Frost away"
|
||||
},
|
||||
"eco_away_temp": {
|
||||
"name": "Eco away"
|
||||
},
|
||||
"comfort_away_temp": {
|
||||
"name": "Comfort away"
|
||||
},
|
||||
"boost_away_temp": {
|
||||
"name": "Boost away"
|
||||
},
|
||||
"eco_ac_away_temp": {
|
||||
"name": "Eco ac away"
|
||||
},
|
||||
"comfort_ac_away_temp": {
|
||||
"name": "Comfort ac away"
|
||||
},
|
||||
"boost_ac_away_temp": {
|
||||
"name": "Boost ac away"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ from .underlyings import UnderlyingValve
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ThermostatOverValve(BaseThermostat):
|
||||
class ThermostatOverValve(BaseThermostat): # pylint: disable=abstract-method
|
||||
"""Representation of a class for a Versatile Thermostat over a Valve"""
|
||||
|
||||
_entity_component_unrecorded_attributes = (
|
||||
|
||||
@@ -12,13 +12,32 @@
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Configure your thermostat. You will be able to finalize the configuration when all needed parameters are valued.",
|
||||
"menu_options": {
|
||||
"main": "Main attributes",
|
||||
"central_boiler": "Central boiler",
|
||||
"type": "Underlyings",
|
||||
"tpi": "TPI parameters",
|
||||
"features": "Features",
|
||||
"presets": "Presets",
|
||||
"window": "Window detection",
|
||||
"motion": "Motion detection",
|
||||
"power": "Power management",
|
||||
"presence": "Presence detection",
|
||||
"advanced": "Advanced parameters",
|
||||
"finalize": "All done",
|
||||
"configuration_not_complete": "Configuration not complete"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Add new Versatile Thermostat",
|
||||
"description": "Main mandatory attributes",
|
||||
"data": {
|
||||
"name": "Name",
|
||||
"thermostat_type": "Thermostat type",
|
||||
"temperature_sensor_entity_id": "Temperature sensor entity id",
|
||||
"temperature_sensor_entity_id": "Room temperature sensor entity id",
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id",
|
||||
"cycle_min": "Cycle duration (minutes)",
|
||||
"temp_min": "Minimal temperature allowed",
|
||||
@@ -26,18 +45,24 @@
|
||||
"step_temperature": "Temperature step",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config). Check to enable the control of the VTherm with the select central_mode entities.",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration. Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"add_central_boiler_control": "Add a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the next configuration page",
|
||||
"use_main_central_config": "Use additional central main configuration. Check to use the central main configuration (outdoor temperature, min, max, step, ...).",
|
||||
"used_by_controls_central_boiler": "Used by central boiler. Check if this VTherm should have control on the central boiler"
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Features",
|
||||
"description": "Thermostat features",
|
||||
"data": {
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Linked entities",
|
||||
"description": "Linked entities attributes",
|
||||
@@ -104,26 +129,9 @@
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
"comfort_temp": "Comfort preset",
|
||||
"boost_temp": "Boost preset",
|
||||
"frost_temp": "Frost protection preset",
|
||||
"eco_ac_temp": "Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
"comfort_temp": "Temperature in Comfort preset",
|
||||
"boost_temp": "Temperature in Boost preset",
|
||||
"frost_temp": "Temperature in Frost protection preset",
|
||||
"eco_ac_temp": "Temperature in Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -189,25 +197,10 @@
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco preset",
|
||||
"comfort_away_temp": "Comfort preset",
|
||||
"boost_away_temp": "Boost preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost pres et in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
"use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
"eco_away_temp": "Temperature in Eco preset when no presence",
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence",
|
||||
"frost_away_temp": "Temperature in Frost protection preset when no presence",
|
||||
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
|
||||
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
"presence_sensor_entity_id": "Presence sensor entity id"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -251,6 +244,25 @@
|
||||
"thermostat_type": "Only one central configuration type is possible"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Configure your thermostat. You will be able to finalize the configuration when all needed parameters are valued.",
|
||||
"menu_options": {
|
||||
"main": "Main attributes",
|
||||
"central_boiler": "Central boiler",
|
||||
"type": "Underlyings",
|
||||
"tpi": "TPI parameters",
|
||||
"features": "Features",
|
||||
"presets": "Presets",
|
||||
"window": "Window detection",
|
||||
"motion": "Motion detection",
|
||||
"power": "Power management",
|
||||
"presence": "Presence detection",
|
||||
"advanced": "Advanced parameters",
|
||||
"finalize": "All done",
|
||||
"configuration_not_complete": "Configuration not complete"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Main - {name}",
|
||||
"description": "Main mandatory attributes",
|
||||
@@ -265,18 +277,24 @@
|
||||
"step_temperature": "Temperature step",
|
||||
"device_power": "Device power",
|
||||
"use_central_mode": "Enable the control by central entity (need central config). Check to enable the control of the VTherm with the select central_mode entities.",
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_main_central_config": "Use central main configuration. Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
|
||||
"add_central_boiler_control": "Add a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the next configuration page",
|
||||
"use_main_central_config": "Use additional central main configuration. Check to use the central main configuration (outdoor temperature, min, max, step, ...).",
|
||||
"used_by_controls_central_boiler": "Used by central boiler. Check if this VTherm should have control on the central boiler"
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Features - {name}",
|
||||
"description": "Thermostat features",
|
||||
"data": {
|
||||
"use_window_feature": "Use window detection",
|
||||
"use_motion_feature": "Use motion detection",
|
||||
"use_power_feature": "Use power management",
|
||||
"use_presence_feature": "Use presence detection",
|
||||
"use_central_boiler_feature": "Use a central boiler. Check to add a control to your central boiler. You will have to configure the VTherm which will have a control of the central boiler after seecting this checkbox to take effect. If one VTherm need heating, the boiler will be turned on. If no VTherm needs heating, the boiler will be turned off. Commands for turning on/off the central boiler are given in the related configuration page"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entities - {name}",
|
||||
"description": "Linked entities attributes",
|
||||
@@ -343,26 +361,9 @@
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets - {name}",
|
||||
"description": "For each preset set the target temperature (0 to ignore preset)",
|
||||
"description": "Check if the thermostat will use central presets. Uncheck and the thermostat will have its own preset entities",
|
||||
"data": {
|
||||
"eco_temp": "Eco preset",
|
||||
"comfort_temp": "Comfort preset",
|
||||
"boost_temp": "Boost preset",
|
||||
"frost_temp": "Frost protection preset",
|
||||
"eco_ac_temp": "Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Boost preset for AC mode",
|
||||
"use_presets_central_config": "Use central presets configuration"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Temperature in Eco preset",
|
||||
"comfort_temp": "Temperature in Comfort preset",
|
||||
"boost_temp": "Temperature in Boost preset",
|
||||
"frost_temp": "Temperature in Frost protection preset",
|
||||
"eco_ac_temp": "Temperature in Eco preset for AC mode",
|
||||
"comfort_ac_temp": "Temperature in Comfort preset for AC mode",
|
||||
"boost_ac_temp": "Temperature in Boost preset for AC mode",
|
||||
"use_presets_central_config": "Check to use the central presets configuration. Uncheck to use a specific presets configuration for this VTherm"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -428,25 +429,10 @@
|
||||
"description": "Presence management attributes.\nGives the a presence sensor of your home (true is someone is present) and give the corresponding temperature preset setting.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Presence sensor",
|
||||
"eco_away_temp": "Eco away preset",
|
||||
"comfort_away_temp": "Comfort away preset",
|
||||
"boost_away_temp": "Boost away preset",
|
||||
"frost_away_temp": "Frost protection preset",
|
||||
"eco_ac_away_temp": "Eco away preset in AC mode",
|
||||
"comfort_ac_away_temp": "Comfort away preset in AC mode",
|
||||
"boost_ac_away_temp": "Boost away preset in AC mode",
|
||||
"use_presence_central_config": "Use central presence configuration"
|
||||
"use_presence_central_config": "Use central presence temperature configuration. Uncheck to use specific temperature entities"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Presence sensor entity id",
|
||||
"eco_away_temp": "Temperature in Eco preset when no presence",
|
||||
"comfort_away_temp": "Temperature in Comfort preset when no presence",
|
||||
"boost_away_temp": "Temperature in Boost preset when no presence",
|
||||
"frost_away_temp": "Temperature in Frost protection preset when no presence",
|
||||
"eco_ac_away_temp": "Temperature in Eco preset when no presence in AC mode",
|
||||
"comfort_ac_away_temp": "Temperature in Comfort preset when no presence in AC mode",
|
||||
"boost_ac_away_temp": "Temperature in Boost preset when no presence in AC mode",
|
||||
"use_presence_central_config": "Check to use the central presence configuration. Uncheck to use a specific presence configuration for this VTherm"
|
||||
"presence_sensor_entity_id": "Presence sensor entity id"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -537,6 +523,53 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"frost_temp": {
|
||||
"name": "Frost"
|
||||
},
|
||||
"eco_temp": {
|
||||
"name": "Eco"
|
||||
},
|
||||
"comfort_temp": {
|
||||
"name": "Comfort"
|
||||
},
|
||||
"boost_temp": {
|
||||
"name": "Boost"
|
||||
},
|
||||
"frost_ac_temp": {
|
||||
"name": "Frost ac"
|
||||
},
|
||||
"eco_ac_temp": {
|
||||
"name": "Eco ac"
|
||||
},
|
||||
"comfort_ac_temp": {
|
||||
"name": "Comfort ac"
|
||||
},
|
||||
"boost_ac_temp": {
|
||||
"name": "Boost ac"
|
||||
},
|
||||
"frost_away_temp": {
|
||||
"name": "Frost away"
|
||||
},
|
||||
"eco_away_temp": {
|
||||
"name": "Eco away"
|
||||
},
|
||||
"comfort_away_temp": {
|
||||
"name": "Comfort away"
|
||||
},
|
||||
"boost_away_temp": {
|
||||
"name": "Boost away"
|
||||
},
|
||||
"eco_ac_away_temp": {
|
||||
"name": "Eco ac away"
|
||||
},
|
||||
"comfort_ac_away_temp": {
|
||||
"name": "Comfort ac away"
|
||||
},
|
||||
"boost_ac_away_temp": {
|
||||
"name": "Boost ac away"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,25 @@
|
||||
"thermostat_type": "Un seul thermostat de type Configuration centrale est possible."
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Paramétrez votre thermostat. Vous pourrez finaliser la configuration quand tous les paramètres auront été saisis.",
|
||||
"menu_options": {
|
||||
"main": "Principaux Attributs",
|
||||
"central_boiler": "Chauffage central",
|
||||
"type": "Sous-jacents",
|
||||
"tpi": "Paramètres TPI",
|
||||
"features": "Fonctions",
|
||||
"presets": "Pre-réglages",
|
||||
"window": "Détection d'ouverture",
|
||||
"motion": "Détection de mouvement",
|
||||
"power": "Gestion de la puissance",
|
||||
"presence": "Détection de présence",
|
||||
"advanced": "Paramètres avancés",
|
||||
"finalize": "Finaliser la création",
|
||||
"configuration_not_complete": "Configuration incomplète"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Ajout d'un nouveau thermostat",
|
||||
"description": "Principaux attributs obligatoires",
|
||||
@@ -26,18 +45,24 @@
|
||||
"step_temperature": "Pas de température",
|
||||
"device_power": "Puissance de l'équipement",
|
||||
"use_central_mode": "Autoriser le controle par une entity centrale ('nécessite une config. centrale`). Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale.",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_main_central_config": "Utiliser la configuration centrale. Cochez pour utiliser la configuration centrale. Décochez et saisissez les attributs pour utiliser une configuration spécifique.",
|
||||
"add_central_boiler_control": "Ajouter une chaudière centrale. Cochez pour ajouter un controle sur une chaudière centrale. Vous devrez ensuite configurer les VTherms qui commande la chaudière centrale pour que cette option prenne effet. Si au moins un des VTherm a besoin de chauffer, la chaudière centrale sera activée. Si aucun VTherm n'a besoin de chauffer, elle sera éteinte. Les commandes pour allumer/éteindre la chaudière centrale sont données dans la page de configuration suivante.",
|
||||
"use_main_central_config": "Utiliser la configuration centrale supplémentaire. Cochez pour utiliser la configuration centrale supplémentaire (température externe, min, max, pas, ...)",
|
||||
"used_by_controls_central_boiler": "Utilisé par la chaudière centrale. Cochez si ce VTherm doit contrôler la chaudière centrale."
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure."
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Fonctions",
|
||||
"description": "Fonctions du thermostat à utiliser",
|
||||
"data": {
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_central_boiler_feature": "Ajouter une chaudière centrale. Cochez pour ajouter un controle sur une chaudière centrale. Vous devrez ensuite configurer les VTherms qui commande la chaudière centrale pour que cette option prenne effet. Si au moins un des VTherm a besoin de chauffer, la chaudière centrale sera activée. Si aucun VTherm n'a besoin de chauffer, elle sera éteinte. Les commandes pour allumer/éteindre la chaudière centrale sont données dans la page de configuration suivante."
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entité(s) liée(s)",
|
||||
"description": "Attributs de(s) l'entité(s) liée(s)",
|
||||
@@ -103,27 +128,10 @@
|
||||
}
|
||||
},
|
||||
"presets": {
|
||||
"title": "Presets",
|
||||
"description": "Pour chaque preset, donnez la température cible (0 pour ignorer le preset)",
|
||||
"title": "Pre-réglages",
|
||||
"description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques",
|
||||
"data": {
|
||||
"eco_temp": "Preset Eco",
|
||||
"comfort_temp": "Preset Comfort",
|
||||
"boost_temp": "Preset Boost",
|
||||
"frost_temp": "Preset Hors-gel",
|
||||
"eco_ac_temp": "Preset Eco en mode AC",
|
||||
"comfort_ac_temp": "Preset Comfort en mode AC",
|
||||
"boost_ac_temp": "Preset Boost en mode AC",
|
||||
"use_presets_central_config": "Utiliser la configuration des presets centrale"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Température en preset Eco",
|
||||
"comfort_temp": "Température en preset Comfort",
|
||||
"boost_temp": "Température en preset Boost",
|
||||
"frost_temp": "Température en preset Hors-gel",
|
||||
"eco_ac_temp": "Température en preset Eco en mode AC",
|
||||
"comfort_ac_temp": "Température en preset Comfort en mode AC",
|
||||
"boost_ac_temp": "Température en preset Boost en mode AC",
|
||||
"use_presets_central_config": "Cochez pour utiliser la configuration des presets centrale. Décochez et saisissez les attributs pour utiliser une configuration des presets spécifique"
|
||||
"use_presets_central_config": "Utiliser la configuration des pré-réglages centrale"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -186,28 +194,13 @@
|
||||
},
|
||||
"presence": {
|
||||
"title": "Gestion de la présence",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence.",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'abs.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Capteur de présence",
|
||||
"eco_away_temp": "preset Eco",
|
||||
"comfort_away_temp": "preset Comfort",
|
||||
"boost_away_temp": "preset Boost",
|
||||
"frost_away_temp": "preset Hors-gel",
|
||||
"eco_ac_away_temp": "preset Eco en mode AC",
|
||||
"comfort_ac_away_temp": "preset Comfort en mode AC",
|
||||
"boost_ac_away_temp": "preset Boost en mode AC",
|
||||
"use_presence_central_config": "Utiliser la configuration centrale de la présence"
|
||||
"use_presence_central_config": "Utiliser la configuration centrale des températures en cas d'absence. Décochez pour avoir des entités de température dédiées"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence",
|
||||
"eco_away_temp": "Température en preset Eco en cas d'absence",
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'absence",
|
||||
"frost_away_temp": "Température en preset Hors-gel en cas d'absence",
|
||||
"eco_ac_away_temp": "Température en preset Eco en cas d'absence en mode AC",
|
||||
"comfort_ac_away_temp": "Température en preset Comfort en cas d'absence en mode AC",
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC",
|
||||
"use_presence_central_config": "Cochez pour utiliser la configuration centrale de la présence. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la présence"
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -263,6 +256,25 @@
|
||||
"thermostat_type": "Un seul thermostat de type Configuration centrale est possible."
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"description": "Paramétrez votre thermostat. Vous pourrez finaliser la configuration quand tous les paramètres auront été saisis.",
|
||||
"menu_options": {
|
||||
"main": "Principaux Attributs",
|
||||
"central_boiler": "Chauffage central",
|
||||
"type": "Sous-jacents",
|
||||
"tpi": "Paramètres TPI",
|
||||
"features": "Fonctions",
|
||||
"presets": "Pre-réglages",
|
||||
"window": "Détection d'ouvertures",
|
||||
"motion": "Détection de mouvement",
|
||||
"power": "Gestion de la puissance",
|
||||
"presence": "Détection de présence",
|
||||
"advanced": "Paramètres avancés",
|
||||
"finalize": "Finaliser les modifications",
|
||||
"configuration_not_complete": "Configuration incomplète"
|
||||
}
|
||||
},
|
||||
"main": {
|
||||
"title": "Attributs - {name}",
|
||||
"description": "Principaux attributs obligatoires",
|
||||
@@ -277,18 +289,24 @@
|
||||
"step_temperature": "Pas de température",
|
||||
"device_power": "Puissance de l'équipement",
|
||||
"use_central_mode": "Autoriser le controle par une entity centrale ('nécessite une config. centrale`). Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale.",
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_main_central_config": "Utiliser la configuration centrale. Cochez pour utiliser la configuration centrale. Décochez et saisissez les attributs pour utiliser une configuration spécifique.",
|
||||
"add_central_boiler_control": "Ajouter une chaudière centrale. Cochez pour ajouter un controle sur une chaudière centrale. Vous devrez ensuite configurer les VTherms qui commande la chaudière centrale pour que cette option prenne effet. Si au moins un des VTherm a besoin de chauffer, la chaudière centrale sera activée. Si aucun VTherm n'a besoin de chauffer, elle sera éteinte. Les commandes pour allumer/éteindre la chaudière centrale sont données dans la page de configuration suivante.",
|
||||
"use_main_central_config": "Utiliser la configuration centrale supplémentaire. Cochez pour utiliser la configuration centrale supplémentaire (température externe, min, max, pas, ...).",
|
||||
"used_by_controls_central_boiler": "Utilisé par la chaudière centrale. Cochez si ce VTherm doit contrôler la chaudière centrale."
|
||||
},
|
||||
"data_description": {
|
||||
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure. N'est pas utilisé si la configuration centrale est utilisée."
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"title": "Fonctions - {name}",
|
||||
"description": "Fonctions du thermostat à utiliser",
|
||||
"data": {
|
||||
"use_window_feature": "Avec détection des ouvertures",
|
||||
"use_motion_feature": "Avec détection de mouvement",
|
||||
"use_power_feature": "Avec gestion de la puissance",
|
||||
"use_presence_feature": "Avec détection de présence",
|
||||
"use_central_boiler_feature": "Ajouter une chaudière centrale. Cochez pour ajouter un controle sur une chaudière centrale. Vous devrez ensuite configurer les VTherms qui commande la chaudière centrale pour que cette option prenne effet. Si au moins un des VTherm a besoin de chauffer, la chaudière centrale sera activée. Si aucun VTherm n'a besoin de chauffer, elle sera éteinte. Les commandes pour allumer/éteindre la chaudière centrale sont données dans la page de configuration suivante."
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"title": "Entités - {name}",
|
||||
"description": "Attributs de(s) l'entité(s) liée(s)",
|
||||
@@ -349,26 +367,9 @@
|
||||
},
|
||||
"presets": {
|
||||
"title": "Pre-réglages - {name}",
|
||||
"description": "Réglage des presets. Donnez la température cible (0 pour ignorer le preset)",
|
||||
"description": "Cochez pour que ce thermostat utilise les pré-réglages de la configuration centrale. Décochez pour utiliser des entités de température spécifiques",
|
||||
"data": {
|
||||
"eco_temp": "Preset Eco",
|
||||
"comfort_temp": "Preset Comfort",
|
||||
"boost_temp": "Preset Boost",
|
||||
"frost_temp": "Preset Hors-gel",
|
||||
"eco_ac_temp": "Preset Eco en mode AC",
|
||||
"comfort_ac_temp": "Preset Comfort en mode AC",
|
||||
"boost_ac_temp": "Preset Boost en mode AC",
|
||||
"use_presets_central_config": "Utiliser la configuration centrale des presets"
|
||||
},
|
||||
"data_description": {
|
||||
"eco_temp": "Température en preset Eco",
|
||||
"comfort_temp": "Température en preset Comfort",
|
||||
"boost_temp": "Température en preset Boost",
|
||||
"frost_temp": "Température en preset Hors-gel",
|
||||
"eco_ac_temp": "Température en preset Eco en mode AC",
|
||||
"comfort_ac_temp": "Température en preset Comfort en mode AC",
|
||||
"boost_ac_temp": "Température en preset Boost en mode AC",
|
||||
"use_presets_central_config": "Cochez pour utiliser la configuration centrale des presets. Décochez et saisissez les attributs pour utiliser une configuration des presets spécifique"
|
||||
"use_presets_central_config": "Utiliser la configuration des pré-réglages centrale"
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
@@ -431,28 +432,13 @@
|
||||
},
|
||||
"presence": {
|
||||
"title": "Présence - {name}",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'absence.",
|
||||
"description": "Donnez un capteur de présence (true si quelqu'un est présent) et les températures cibles à utiliser en cas d'abs.",
|
||||
"data": {
|
||||
"presence_sensor_entity_id": "Capteur de présence",
|
||||
"eco_away_temp": "preset Eco",
|
||||
"comfort_away_temp": "preset Comfort",
|
||||
"boost_away_temp": "preset Boost",
|
||||
"frost_away_temp": "preset Hors-gel",
|
||||
"eco_ac_away_temp": "preset Eco en mode AC",
|
||||
"comfort_ac_away_temp": "preset Comfort en mode AC",
|
||||
"boost_ac_away_temp": "preset Boost en mode AC",
|
||||
"use_presence_central_config": "Utiliser la configuration centrale de la présence"
|
||||
"use_presence_central_config": "Utiliser la configuration centrale des températures en cas d'absence. Décochez pour avoir des entités de température dédiées"
|
||||
},
|
||||
"data_description": {
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence",
|
||||
"eco_away_temp": "Température en preset Eco en cas d'absence",
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'absence",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'absence",
|
||||
"frost_away_temp": "Température en preset Hors-gel en cas d'absence",
|
||||
"eco_ac_away_temp": "Température en preset Eco en cas d'absence en mode AC",
|
||||
"comfort_ac_away_temp": "Température en preset Comfort en cas d'absence en mode AC",
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'absence en mode AC",
|
||||
"use_presence_central_config": "Cochez pour utiliser la configuration centrale de la présence. Décochez et saisissez les attributs pour utiliser une configuration spécifique de la présence"
|
||||
"presence_sensor_entity_id": "Id d'entité du capteur de présence"
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
@@ -555,6 +541,53 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"frost_temp": {
|
||||
"name": "Hors gel "
|
||||
},
|
||||
"eco_temp": {
|
||||
"name": "Eco"
|
||||
},
|
||||
"comfort_temp": {
|
||||
"name": "Confort"
|
||||
},
|
||||
"boost_temp": {
|
||||
"name": "Boost"
|
||||
},
|
||||
"frost_ac_temp": {
|
||||
"name": "Hors gel clim"
|
||||
},
|
||||
"eco_ac_temp": {
|
||||
"name": "Eco clim"
|
||||
},
|
||||
"comfort_ac_temp": {
|
||||
"name": "Confort clim"
|
||||
},
|
||||
"boost_ac_temp": {
|
||||
"name": "Boost clim"
|
||||
},
|
||||
"frost_away_temp": {
|
||||
"name": "Hors gel abs"
|
||||
},
|
||||
"eco_away_temp": {
|
||||
"name": "Eco abs"
|
||||
},
|
||||
"comfort_away_temp": {
|
||||
"name": "Confort abs"
|
||||
},
|
||||
"boost_away_temp": {
|
||||
"name": "Boost abs"
|
||||
},
|
||||
"eco_ac_away_temp": {
|
||||
"name": "Eco clim abs"
|
||||
},
|
||||
"comfort_ac_away_temp": {
|
||||
"name": "Confort clim abs"
|
||||
},
|
||||
"boost_ac_away_temp": {
|
||||
"name": "Boost clim abs"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
""" The API of Versatile Thermostat"""
|
||||
|
||||
import logging
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.number import NumberEntity
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
CONF_AUTO_REGULATION_EXPERT,
|
||||
@@ -51,19 +56,24 @@ class VersatileThermostatAPI(dict):
|
||||
self._central_boiler_entity = None
|
||||
self._threshold_number_entity = None
|
||||
self._nb_active_number_entity = None
|
||||
self._central_configuration = None
|
||||
# A dict that will store all Number entities which holds the temperature
|
||||
self._number_temperatures = dict()
|
||||
|
||||
def find_central_configuration(self):
|
||||
"""Search for a central configuration"""
|
||||
for config_entry in VersatileThermostatAPI._hass.config_entries.async_entries(
|
||||
DOMAIN
|
||||
):
|
||||
if (
|
||||
config_entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
== CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
):
|
||||
central_config = config_entry
|
||||
return central_config
|
||||
return None
|
||||
if not self._central_configuration:
|
||||
for (
|
||||
config_entry
|
||||
) in VersatileThermostatAPI._hass.config_entries.async_entries(DOMAIN):
|
||||
if (
|
||||
config_entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
== CONF_THERMOSTAT_CENTRAL_CONFIG
|
||||
):
|
||||
self._central_configuration = config_entry
|
||||
break
|
||||
# return self._central_configuration
|
||||
return self._central_configuration
|
||||
|
||||
def add_entry(self, entry: ConfigEntry):
|
||||
"""Add a new entry"""
|
||||
@@ -106,10 +116,64 @@ class VersatileThermostatAPI(dict):
|
||||
):
|
||||
"""register the two number entities needed for boiler activation"""
|
||||
self._threshold_number_entity = threshold_number_entity
|
||||
# If sensor and threshold number are initialized, reload the listener
|
||||
# if self._nb_active_number_entity and self._central_boiler_entity:
|
||||
# self._hass.async_add_job(self.reload_central_boiler_binary_listener)
|
||||
|
||||
def register_nb_device_active_boiler(self, nb_active_number_entity):
|
||||
"""register the two number entities needed for boiler activation"""
|
||||
self._nb_active_number_entity = nb_active_number_entity
|
||||
# if self._threshold_number_entity and self._central_boiler_entity:
|
||||
# self._hass.async_add_job(self.reload_central_boiler_binary_listener)
|
||||
|
||||
def register_temperature_number(
|
||||
self,
|
||||
config_id: str,
|
||||
preset_name: str,
|
||||
number_entity: NumberEntity,
|
||||
):
|
||||
"""Register the NumberEntity for a particular device / preset."""
|
||||
# Search for device_name into the _number_temperatures dict
|
||||
if not self._number_temperatures.get(config_id):
|
||||
self._number_temperatures[config_id] = dict()
|
||||
|
||||
self._number_temperatures.get(config_id)[preset_name] = number_entity
|
||||
|
||||
def get_temperature_number_value(self, config_id, preset_name) -> float | None:
|
||||
"""Returns the value of a previously registred NumberEntity which represent
|
||||
a temperature. If no NumberEntity was previously registred, then returns None"""
|
||||
entities = self._number_temperatures.get(config_id, None)
|
||||
if entities:
|
||||
entity = entities.get(preset_name, None)
|
||||
if entity:
|
||||
return entity.state
|
||||
return None
|
||||
|
||||
async def init_vtherm_links(self, only_use_central=False):
|
||||
"""INitialize all VTherms entities links
|
||||
This method is called when HA is fully started (and all entities should be initialized)
|
||||
Or when we need to reload all VTherm links (with Number temp entities, central boiler, ...)
|
||||
"""
|
||||
await self.reload_central_boiler_binary_listener()
|
||||
await self.reload_central_boiler_entities_list()
|
||||
# Initialization of all preset for all VTherm
|
||||
component: EntityComponent[ClimateEntity] = self._hass.data.get(
|
||||
CLIMATE_DOMAIN, None
|
||||
)
|
||||
if component:
|
||||
for entity in component.entities:
|
||||
if hasattr(entity, "init_presets"):
|
||||
if (
|
||||
only_use_central is False
|
||||
or entity.use_central_config_temperature
|
||||
):
|
||||
await entity.init_presets(self.find_central_configuration())
|
||||
|
||||
async def reload_central_boiler_binary_listener(self):
|
||||
"""Reloads the BinarySensor entity which listen to the number of
|
||||
active devices and the thresholds entities"""
|
||||
if self._central_boiler_entity:
|
||||
await self._central_boiler_entity.listen_nb_active_vtherm_entity()
|
||||
|
||||
async def reload_central_boiler_entities_list(self):
|
||||
"""Reload the central boiler list of entities if a central boiler is used"""
|
||||
|
||||
Reference in New Issue
Block a user