Init temperature number for central configuration + testus ok
This commit is contained in:
@@ -11,11 +11,11 @@
|
||||
// "postCreateCommand": "container install",
|
||||
"postCreateCommand": "./container dev-setup",
|
||||
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
|
||||
// uncomment this to get the versatile-thermostat-ui-card
|
||||
"source=${localEnv:HOME}/SugarSync/Projets/home-assistant/versatile-thermostat-ui-card/dist,target=/workspaces/versatile_thermostat/config/www/community/versatile-thermostat-ui-card,type=bind,consistency=cached"
|
||||
],
|
||||
"mounts": [
|
||||
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
|
||||
// uncomment this to get the versatile-thermostat-ui-card
|
||||
"source=${localEnv:HOME}/SugarSync/Projets/home-assistant/versatile-thermostat-ui-card/dist,target=/workspaces/versatile_thermostat/config/www/community/versatile-thermostat-ui-card,type=bind,consistency=cached"
|
||||
],
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
@@ -65,4 +65,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -15,7 +15,5 @@
|
||||
// "/home/vscode/core",
|
||||
"/workspaces/versatile_thermostat/custom_components/versatile_thermostat",
|
||||
"/home/vscode/.local/lib/python3.12/site-packages/homeassistant"
|
||||
],
|
||||
"python.experiments.optOutFrom": ["pythonTestAdapter"],
|
||||
"python.formatting.provider": "none"
|
||||
]
|
||||
}
|
||||
@@ -8,10 +8,10 @@ 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
|
||||
|
||||
@@ -82,15 +82,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,
|
||||
|
||||
@@ -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,
|
||||
@@ -137,6 +137,8 @@ from .prop_algorithm import PropAlgorithm
|
||||
from .open_window_algorithm import WindowOpenDetectionAlgorithm
|
||||
from .ema import ExponentialMovingAverage
|
||||
|
||||
from .temp_number import TemperatureNumber
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
ConfigData = MappingProxyType[str, Any]
|
||||
|
||||
@@ -285,6 +287,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
self._last_central_mode = None
|
||||
self._is_used_by_central_boiler = False
|
||||
|
||||
self._support_flags = None
|
||||
self._attr_preset_modes: list[str] | None
|
||||
|
||||
self.post_init(entry_infos)
|
||||
|
||||
def clean_central_config_doublon(
|
||||
@@ -358,33 +363,36 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
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
|
||||
)
|
||||
# 354 - presets are now initializesd by number entities
|
||||
# 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
|
||||
)
|
||||
# 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()
|
||||
@@ -462,15 +470,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 +537,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
|
||||
@@ -658,6 +643,41 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
await self.async_startup()
|
||||
|
||||
# TODO remove this
|
||||
def init_temperature_preset(self, preset, temperature, is_ac, is_away):
|
||||
"""Initialize the internal temperature preset
|
||||
from the Number entity which holds the temperature"""
|
||||
|
||||
if temperature is None or preset is None:
|
||||
return
|
||||
|
||||
if is_away:
|
||||
self._presets_away[preset] = temperature
|
||||
else:
|
||||
self._presets[preset] = temperature
|
||||
|
||||
_LOGGER.debug(
|
||||
"%s - presets are set to: %s, away: %s",
|
||||
self,
|
||||
self._presets,
|
||||
self._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")
|
||||
|
||||
def remove_thermostat(self):
|
||||
"""Called when the thermostat will be removed"""
|
||||
_LOGGER.info("%s - Removing thermostat", self)
|
||||
@@ -852,10 +872,8 @@ 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 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:
|
||||
@@ -1323,9 +1341,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"""
|
||||
@@ -1344,9 +1362,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
return self._power_temp
|
||||
if preset_mode == PRESET_ACTIVITY:
|
||||
return self._presets[
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
(
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
)
|
||||
]
|
||||
else:
|
||||
# Select _ac presets if in COOL Mode (or over_switch with _ac_mode)
|
||||
@@ -1359,9 +1379,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
STATE_ON,
|
||||
STATE_HOME,
|
||||
]:
|
||||
return self._presets[preset_mode]
|
||||
return self._presets.get(preset_mode, 0)
|
||||
else:
|
||||
return self._presets_away[self.get_preset_away_name(preset_mode)]
|
||||
return self._presets_away.get(self.get_preset_away_name(preset_mode), 0)
|
||||
|
||||
def get_preset_away_name(self, preset_mode: str) -> str:
|
||||
"""Get the preset name in away mode (when presence is off)"""
|
||||
@@ -1812,9 +1832,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
await self._async_internal_set_temperature(
|
||||
self._presets[
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
(
|
||||
self._motion_preset
|
||||
if self._motion_state == STATE_ON
|
||||
else self._no_motion_preset
|
||||
)
|
||||
]
|
||||
)
|
||||
_LOGGER.debug(
|
||||
@@ -2431,21 +2453,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 +2648,19 @@ 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)
|
||||
|
||||
def get_temperature_number_entities(self, config_entry: ConfigData):
|
||||
"""Creates all TemperatureNumber depending of the configuration of the Climate"""
|
||||
|
||||
# TODO add the list of preset we want to use in the VTherm. Here we will suppose all preset will be available
|
||||
entity = TemperatureNumber(
|
||||
self._hass,
|
||||
unique_id=config_entry.entry_id,
|
||||
name=config_entry.data.get(CONF_NAME),
|
||||
preset_name="comfort",
|
||||
is_ac=False,
|
||||
is_away=False,
|
||||
entry_infos=config_entry.data,
|
||||
)
|
||||
|
||||
return entity
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" Implements the VersatileThermostat climate component """
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
@@ -75,7 +76,7 @@ async def async_setup_entry(
|
||||
elif vt_type == CONF_THERMOSTAT_VALVE:
|
||||
entity = ThermostatOverValve(hass, unique_id, name, entry.data)
|
||||
|
||||
async_add_entities([entity], True)
|
||||
async_add_entities([entity, entity.get_temperature_number_entities(entry)], True)
|
||||
|
||||
# Add services
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
""" Implements the VersatileThermostat select component """
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
# from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, CoreState # , callback
|
||||
@@ -11,10 +10,7 @@ from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberMode,
|
||||
NumberDeviceClass,
|
||||
ATTR_MAX,
|
||||
ATTR_MIN,
|
||||
ATTR_STEP,
|
||||
ATTR_MODE,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.climate import (
|
||||
PRESET_BOOST,
|
||||
@@ -29,7 +25,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
from .commons import VersatileThermostatBaseEntity
|
||||
|
||||
from .const import (
|
||||
@@ -50,8 +46,8 @@ from .const import (
|
||||
PRESET_AC_SUFFIX,
|
||||
CONF_PRESETS_VALUES,
|
||||
CONF_PRESETS_WITH_AC_VALUES,
|
||||
# CONF_PRESETS_AWAY_VALUES,
|
||||
# CONF_PRESETS_AWAY_WITH_AC_VALUES,
|
||||
CONF_PRESETS_AWAY_VALUES,
|
||||
CONF_PRESETS_AWAY_WITH_AC_VALUES,
|
||||
overrides,
|
||||
)
|
||||
|
||||
@@ -63,6 +59,13 @@ PRESET_ICON_MAPPING = {
|
||||
PRESET_ECO_AC + "_temp": "mdi:leaf-circle-outline",
|
||||
PRESET_COMFORT_AC + "_temp": "mdi:sofa-outline",
|
||||
PRESET_BOOST_AC + "_temp": "mdi:rocket-launch-outline",
|
||||
PRESET_FROST_PROTECTION + "_away_temp": "mdi:snowflake-thermometer",
|
||||
PRESET_ECO + "_away_temp": "mdi:leaf",
|
||||
PRESET_COMFORT + "_away_temp": "mdi:sofa",
|
||||
PRESET_BOOST + "_away_temp": "mdi:rocket-launch",
|
||||
PRESET_ECO_AC + "_away_temp": "mdi:leaf-circle-outline",
|
||||
PRESET_COMFORT_AC + "_away_temp": "mdi:sofa-outline",
|
||||
PRESET_BOOST_AC + "_away_temp": "mdi:rocket-launch-outline",
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -85,21 +88,54 @@ async def async_setup_entry(
|
||||
|
||||
entities = []
|
||||
|
||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG or not is_central_boiler:
|
||||
for preset in CONF_PRESETS_VALUES:
|
||||
entities.append(
|
||||
TemperatureNumber(hass, unique_id, preset, preset, entry.data)
|
||||
)
|
||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
if not is_central_boiler:
|
||||
pass
|
||||
# for preset in CONF_PRESETS_VALUES:
|
||||
# entities.append(
|
||||
# TemperatureNumber(
|
||||
# hass, unique_id, preset, 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, preset, preset, entry.data)
|
||||
)
|
||||
# TODO
|
||||
# if entry.data.get(CONF_AC_MODE, False):
|
||||
# for preset in CONF_PRESETS_WITH_AC_VALUES:
|
||||
# entities.append(
|
||||
# TemperatureNumber(
|
||||
# hass, unique_id, preset, preset, True, False, entry.data
|
||||
# )
|
||||
# )
|
||||
else:
|
||||
entities.append(
|
||||
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data)
|
||||
)
|
||||
for preset in CONF_PRESETS_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, False, False, entry.data
|
||||
)
|
||||
)
|
||||
for preset in CONF_PRESETS_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, True, False, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
for preset in CONF_PRESETS_AWAY_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, False, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
for preset in CONF_PRESETS_AWAY_WITH_AC_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, True, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
@@ -171,25 +207,173 @@ class ActivateBoilerThresholdNumber(
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
|
||||
class CentralConfigTemperatureNumber(NumberEntity, RestoreEntity):
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
# _attr_translation_key = "temperature"
|
||||
|
||||
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 = entry_infos.get(CONF_NAME)
|
||||
# self._attr_name = name
|
||||
|
||||
self._attr_translation_key = preset_name
|
||||
# self._attr_translation_placeholders = {
|
||||
# "preset": preset_name,
|
||||
# "ac": "-AC" if is_ac else "",
|
||||
# "away": "-AWAY" if is_away else "",
|
||||
# }
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.central_configuration_{preset_name}"
|
||||
self._attr_unique_id = f"central_configuration_{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):
|
||||
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:
|
||||
"""Change the value"""
|
||||
|
||||
# TODO implements the native value change -> reload values for all central config
|
||||
# based VTherm
|
||||
# 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
|
||||
# self.hass.create_task(
|
||||
# self.my_climate.service_set_preset_temperature(
|
||||
# self._preset_name.replace("_temp", ""), self._attr_native_value, None
|
||||
# )
|
||||
|
||||
# )
|
||||
|
||||
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
|
||||
_attr_translation_key = "temperature"
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, unique_id, name, preset_name, entry_infos
|
||||
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, entry_infos.get(CONF_NAME))
|
||||
|
||||
split = name.split("_")
|
||||
self._attr_name = split[0]
|
||||
if "_" + split[1] == PRESET_AC_SUFFIX:
|
||||
self._attr_name = self._attr_name + " AC"
|
||||
# self._attr_name = split[0]
|
||||
# if "_" + split[1] == PRESET_AC_SUFFIX:
|
||||
# self._attr_name = self._attr_name + " AC"
|
||||
|
||||
self._attr_name = self._attr_name + " temperature"
|
||||
# self._attr_name = self._attr_name + " temperature"
|
||||
|
||||
self._attr_translation_placeholders = {
|
||||
"preset": preset_name,
|
||||
"ac": "-AC" if is_ac else "",
|
||||
"away": "-AWAY" if is_away else "",
|
||||
}
|
||||
self._attr_unique_id = f"{self._device_name}_{name}"
|
||||
self._attr_device_class = NumberDeviceClass.TEMPERATURE
|
||||
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
@@ -203,6 +387,8 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
|
||||
self._attr_mode = NumberMode.BOX
|
||||
self._preset_name = preset_name
|
||||
self._is_away = is_away
|
||||
self._is_ac = is_ac
|
||||
|
||||
self._attr_native_step = entry_infos.get(CONF_STEP_TEMPERATURE, 0.5)
|
||||
self._attr_native_min_value = entry_infos.get(CONF_TEMP_MIN)
|
||||
@@ -220,8 +406,11 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
_LOGGER.debug(
|
||||
"%s - Calling async_added_to_hass old_state is %s", self, old_state
|
||||
)
|
||||
if old_state is not None:
|
||||
self._attr_value = self._attr_native_value = float(old_state.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):
|
||||
@@ -229,6 +418,11 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
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
|
||||
|
||||
# Initialize the internal temp value of VTherm
|
||||
self.my_climate.init_temperature_preset(
|
||||
self._preset_name, self._attr_native_value, self._is_ac, self._is_away
|
||||
)
|
||||
return
|
||||
|
||||
# @overrides
|
||||
@@ -238,8 +432,15 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
# return self.my_climate.target_temperature_step
|
||||
|
||||
@overrides
|
||||
def set_native_value(self, value: float) -> None:
|
||||
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)
|
||||
|
||||
@@ -248,6 +449,15 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
|
||||
self._attr_value = self._attr_native_value = float_value
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
# Update the VTherm
|
||||
self.hass.create_task(
|
||||
self.my_climate.service_set_preset_temperature(
|
||||
self._preset_name.replace("_temp", ""), self._attr_native_value, None
|
||||
)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
|
||||
@@ -537,6 +537,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"temperature": {
|
||||
"name": "{preset}{ac}-{away} [%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
197
custom_components/versatile_thermostat/temp_number.py
Normal file
197
custom_components/versatile_thermostat/temp_number.py
Normal file
@@ -0,0 +1,197 @@
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
""" Implements the VersatileThermostat select component """
|
||||
import logging
|
||||
|
||||
# from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.core import HomeAssistant, CoreState # , callback
|
||||
|
||||
from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberMode,
|
||||
NumberDeviceClass,
|
||||
)
|
||||
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 .const import (
|
||||
DOMAIN,
|
||||
DEVICE_MANUFACTURER,
|
||||
CONF_NAME,
|
||||
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_AC_SUFFIX,
|
||||
CONF_PRESETS_VALUES,
|
||||
CONF_PRESETS_WITH_AC_VALUES,
|
||||
# CONF_PRESETS_AWAY_VALUES,
|
||||
# CONF_PRESETS_AWAY_WITH_AC_VALUES,
|
||||
overrides,
|
||||
)
|
||||
|
||||
PRESET_ICON_MAPPING = {
|
||||
PRESET_FROST_PROTECTION + "_temp": "mdi:snowflake-thermometer",
|
||||
PRESET_ECO + "_temp": "mdi:leaf",
|
||||
PRESET_COMFORT + "_temp": "mdi:sofa",
|
||||
PRESET_BOOST + "_temp": "mdi:rocket-launch",
|
||||
PRESET_ECO_AC + "_temp": "mdi:leaf-circle-outline",
|
||||
PRESET_COMFORT_AC + "_temp": "mdi:sofa-outline",
|
||||
PRESET_BOOST_AC + "_temp": "mdi:rocket-launch-outline",
|
||||
}
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TemperatureNumber(NumberEntity, RestoreEntity):
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "temperature"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
unique_id,
|
||||
name,
|
||||
preset_name,
|
||||
is_ac,
|
||||
is_away,
|
||||
entry_infos: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the temperature with entry_infos if available. Else
|
||||
the restoration will do the trick."""
|
||||
# super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
|
||||
|
||||
self.my_climate = None
|
||||
self._unique_id = unique_id
|
||||
self._device_name = entry_infos.get(CONF_NAME)
|
||||
|
||||
# split = name.split("_")
|
||||
# self._attr_name = split[0]
|
||||
# if "_" + split[1] == PRESET_AC_SUFFIX:
|
||||
# self._attr_name = self._attr_name + " AC"
|
||||
|
||||
self._attr_name = preset_name + " new temperature"
|
||||
|
||||
# self._attr_translation_placeholders = {
|
||||
# "preset": preset_name,
|
||||
# "ac": "-AC" if is_ac else "",
|
||||
# "away": "-AWAY" if is_away else "",
|
||||
# }
|
||||
self._attr_unique_id = f"{self._device_name}_{self._attr_name}"
|
||||
self._attr_device_class = NumberDeviceClass.TEMPERATURE
|
||||
self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
|
||||
|
||||
# Initialize the values if included into the entry_infos. This will do
|
||||
# the temperature migration.
|
||||
# TODO see if this should be replace by the central config if any
|
||||
temp = None
|
||||
# if temp := entry_infos.get(preset_name, None):
|
||||
# self._attr_value = self._attr_native_value = temp
|
||||
|
||||
self._attr_mode = NumberMode.BOX
|
||||
self._preset_name = preset_name
|
||||
self._is_away = is_away
|
||||
self._is_ac = is_ac
|
||||
|
||||
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)
|
||||
|
||||
@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._unique_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()
|
||||
|
||||
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
|
||||
|
||||
# Initialize the internal temp value of VTherm
|
||||
self.my_climate.init_temperature_preset(
|
||||
self._preset_name, self._attr_native_value, self._is_ac, self._is_away
|
||||
)
|
||||
return
|
||||
|
||||
# @overrides
|
||||
# @property
|
||||
# def native_step(self) -> float | None:
|
||||
# """The native step"""
|
||||
# return self.my_climate.target_temperature_step
|
||||
|
||||
@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
|
||||
self.hass.create_task(
|
||||
self.my_climate.service_set_preset_temperature(
|
||||
self._preset_name.replace("_temp", ""), self._attr_native_value, 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
|
||||
@@ -537,6 +537,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"temperature": {
|
||||
"name": "{preset}{ac}-{away} [%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@
|
||||
},
|
||||
"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",
|
||||
@@ -200,13 +200,13 @@
|
||||
},
|
||||
"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",
|
||||
"eco_away_temp": "Température en preset Eco en cas d'abs",
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'abs",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'abs",
|
||||
"frost_away_temp": "Température en preset Hors-gel en cas d'abs",
|
||||
"eco_ac_away_temp": "Température en preset Eco en cas d'abs en mode AC",
|
||||
"comfort_ac_away_temp": "Température en preset Comfort en cas d'abs en mode AC",
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'abs 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"
|
||||
}
|
||||
},
|
||||
@@ -431,7 +431,7 @@
|
||||
},
|
||||
"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",
|
||||
@@ -445,13 +445,13 @@
|
||||
},
|
||||
"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",
|
||||
"eco_away_temp": "Température en preset Eco en cas d'abs",
|
||||
"comfort_away_temp": "Température en preset Comfort en cas d'abs",
|
||||
"boost_away_temp": "Température en preset Boost en cas d'abs",
|
||||
"frost_away_temp": "Température en preset Hors-gel en cas d'abs",
|
||||
"eco_ac_away_temp": "Température en preset Eco en cas d'abs en mode AC",
|
||||
"comfort_ac_away_temp": "Température en preset Comfort en cas d'abs en mode AC",
|
||||
"boost_ac_away_temp": "Température en preset Boost en cas d'abs 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"
|
||||
}
|
||||
},
|
||||
@@ -555,6 +555,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"temperature": {
|
||||
"name": "{preset}{ac}-{away} [%key:component::sensor::entity_component::temperature::name%]"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@ 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, DOMAIN as NUMBER_DOMAIN
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
CONF_AUTO_REGULATION_EXPERT,
|
||||
@@ -52,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"""
|
||||
@@ -108,14 +117,51 @@ 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)
|
||||
# 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)
|
||||
# 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):
|
||||
"""INitialize all VTherms entities links
|
||||
This method is called when HA is fully started (and all entities should be initialized)
|
||||
"""
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -25,5 +25,9 @@ fi
|
||||
## without resulting to symlinks.
|
||||
export PYTHONPATH="${PYTHONPATH}:${PWD}/custom_components"
|
||||
|
||||
## Link custom_components into config
|
||||
rm -f ${PWD}/config/custom_components
|
||||
ln -s ${PWD}/custom_components ${PWD}/config/
|
||||
|
||||
# Start Home Assistant
|
||||
hass --config "${PWD}/config" --debug
|
||||
@@ -528,6 +528,7 @@ def search_entity(hass: HomeAssistant, entity_id, domain) -> Entity:
|
||||
"""Search and return the entity in the domain"""
|
||||
component = hass.data[domain]
|
||||
for entity in component.entities:
|
||||
_LOGGER.debug("Found %s entity: %s", domain, entity.entity_id)
|
||||
if entity.entity_id == entity_id:
|
||||
return entity
|
||||
return None
|
||||
|
||||
396
tests/test_temp_number.py
Normal file
396
tests/test_temp_number.py
Normal file
@@ -0,0 +1,396 @@
|
||||
""" Test the NumberEntity taht holds the temperature of a VTherm or of a Central configuration """
|
||||
|
||||
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
|
||||
|
||||
# from unittest.mock import patch, call
|
||||
# from datetime import datetime, timedelta
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
||||
# from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.components.number import NumberEntity, DOMAIN as NUMBER_DOMAIN
|
||||
|
||||
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
||||
|
||||
# from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
|
||||
from .commons import *
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_central_config(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the construction of a central configuration and the
|
||||
creation and registration of the NumberEntity which holds
|
||||
the temperature initialized from config_entry"""
|
||||
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
temps = {
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17.1,
|
||||
"comfort_temp": 18.1,
|
||||
"boost_temp": 19.1,
|
||||
"eco_ac_temp": 25.1,
|
||||
"comfort_ac_temp": 23.1,
|
||||
"boost_ac_temp": 21.1,
|
||||
"frost_away_temp": 15.1,
|
||||
"eco_away_temp": 15.2,
|
||||
"comfort_away_temp": 15.3,
|
||||
"boost_away_temp": 15.4,
|
||||
"eco_ac_away_temp": 30.5,
|
||||
"comfort_ac_away_temp": 30.6,
|
||||
"boost_ac_away_temp": 30.7,
|
||||
}
|
||||
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_central_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_central_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL: False,
|
||||
}
|
||||
| temps,
|
||||
)
|
||||
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.central_configuration_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity
|
||||
assert temp_entity.state == value
|
||||
|
||||
# This test is dependent to translation en.json. If translations change
|
||||
# this may fails
|
||||
assert (
|
||||
temp_entity.name.lower()
|
||||
== preset_name.replace("_temp", "")
|
||||
.replace("_ac", " ac")
|
||||
.replace("_away", " away")
|
||||
.lower()
|
||||
)
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=central_config_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_central_config_without_temp(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the construction of a central configuration and the
|
||||
creation and registration of the NumberEntity which holds
|
||||
the temperature not intialized from confif_entry.
|
||||
In non AC_MODE the value should be initialized to the MIN"""
|
||||
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
# Default is min Value in non AC_MODE
|
||||
temps = {
|
||||
"frost_temp": 15.0,
|
||||
"eco_temp": 15.0,
|
||||
"comfort_temp": 15.0,
|
||||
"boost_temp": 15.0,
|
||||
"eco_ac_temp": 15.0,
|
||||
"comfort_ac_temp": 15.0,
|
||||
"boost_ac_temp": 15.0,
|
||||
"frost_away_temp": 15.0,
|
||||
"eco_away_temp": 15.0,
|
||||
"comfort_away_temp": 15.0,
|
||||
"boost_away_temp": 15.0,
|
||||
"eco_ac_away_temp": 15.0,
|
||||
"comfort_ac_away_temp": 15.0,
|
||||
"boost_ac_away_temp": 15.0,
|
||||
}
|
||||
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_central_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_central_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL: False,
|
||||
},
|
||||
# | temps,
|
||||
)
|
||||
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.central_configuration_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity
|
||||
assert temp_entity.state == value
|
||||
|
||||
# This test is dependent to translation en.json. If translations change
|
||||
# this may fails
|
||||
assert (
|
||||
temp_entity.name.lower()
|
||||
== preset_name.replace("_temp", "")
|
||||
.replace("_ac", " ac")
|
||||
.replace("_away", " away")
|
||||
.lower()
|
||||
)
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=central_config_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_central_config_without_temp_ac_mode(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the construction of a central configuration and the
|
||||
creation and registration of the NumberEntity which holds
|
||||
the temperature not intialized from confif_entry.
|
||||
In AC_MODE the defaul value should the MAX"""
|
||||
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
# Default is min Value in non AC_MODE
|
||||
temps = {
|
||||
"frost_temp": 30.0,
|
||||
"eco_temp": 30.0,
|
||||
"comfort_temp": 30.0,
|
||||
"boost_temp": 30.0,
|
||||
"eco_ac_temp": 30.0,
|
||||
"comfort_ac_temp": 30.0,
|
||||
"boost_ac_temp": 30.0,
|
||||
"frost_away_temp": 30.0,
|
||||
"eco_away_temp": 30.0,
|
||||
"comfort_away_temp": 30.0,
|
||||
"boost_away_temp": 30.0,
|
||||
"eco_ac_away_temp": 30.0,
|
||||
"comfort_ac_away_temp": 30.0,
|
||||
"boost_ac_away_temp": 30.0,
|
||||
}
|
||||
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_central_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_AC_MODE: True,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_central_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL: False,
|
||||
},
|
||||
# | temps,
|
||||
)
|
||||
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.central_configuration_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity
|
||||
assert temp_entity.state == value
|
||||
|
||||
# This test is dependent to translation en.json. If translations change
|
||||
# this may fails
|
||||
assert (
|
||||
temp_entity.name.lower()
|
||||
== preset_name.replace("_temp", "")
|
||||
.replace("_ac", " ac")
|
||||
.replace("_away", " away")
|
||||
.lower()
|
||||
)
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=central_config_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_central_config_without_temp_restore(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the construction of a central configuration and the
|
||||
creation and registration of the NumberEntity which holds
|
||||
the temperature not intialized from confif_entry"""
|
||||
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
# Default is min Value in non AC_MODE
|
||||
temps = {
|
||||
"frost_temp": 23.0,
|
||||
"eco_temp": 23.0,
|
||||
"comfort_temp": 23.0,
|
||||
"boost_temp": 23.0,
|
||||
"eco_ac_temp": 23.0,
|
||||
"comfort_ac_temp": 23.0,
|
||||
"boost_ac_temp": 23.0,
|
||||
"frost_away_temp": 23.0,
|
||||
"eco_away_temp": 23.0,
|
||||
"comfort_away_temp": 23.0,
|
||||
"boost_away_temp": 23.0,
|
||||
"eco_ac_away_temp": 23.0,
|
||||
"comfort_ac_away_temp": 23.0,
|
||||
"boost_ac_away_temp": 23.0,
|
||||
}
|
||||
|
||||
central_config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: CENTRAL_CONFIG_NAME,
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_central_ext_temp_sensor",
|
||||
CONF_TEMP_MIN: 15,
|
||||
CONF_TEMP_MAX: 30,
|
||||
CONF_AC_MODE: False,
|
||||
CONF_TPI_COEF_INT: 0.5,
|
||||
CONF_TPI_COEF_EXT: 0.02,
|
||||
CONF_WINDOW_DELAY: 15,
|
||||
CONF_WINDOW_AUTO_OPEN_THRESHOLD: 4,
|
||||
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 1,
|
||||
CONF_WINDOW_AUTO_MAX_DURATION: 31,
|
||||
CONF_MOTION_DELAY: 31,
|
||||
CONF_MOTION_OFF_DELAY: 301,
|
||||
CONF_MOTION_PRESET: "boost",
|
||||
CONF_NO_MOTION_PRESET: "frost",
|
||||
CONF_POWER_SENSOR: "sensor.mock_central_power_sensor",
|
||||
CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor",
|
||||
CONF_PRESET_POWER: 14,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 11,
|
||||
CONF_SECURITY_DELAY_MIN: 61,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.5,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2,
|
||||
CONF_ADD_CENTRAL_BOILER_CONTROL: False,
|
||||
},
|
||||
# | temps,
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state",
|
||||
return_value=State(entity_id="number.mock_valve", state="23"),
|
||||
):
|
||||
central_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(central_config_entry.entry_id)
|
||||
|
||||
assert central_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
# We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.central_configuration_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity
|
||||
assert temp_entity.state == value
|
||||
|
||||
# This test is dependent to translation en.json. If translations change
|
||||
# this may fails
|
||||
assert (
|
||||
temp_entity.name.lower()
|
||||
== preset_name.replace("_temp", "")
|
||||
.replace("_ac", " ac")
|
||||
.replace("_away", " away")
|
||||
.lower()
|
||||
)
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=central_config_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
Reference in New Issue
Block a user