With calculation of VTherm temp entities + test ok
This commit is contained in:
@@ -137,8 +137,6 @@ 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]
|
||||
|
||||
@@ -216,6 +214,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
super().__init__()
|
||||
|
||||
self._hass = hass
|
||||
self._entry_infos = None
|
||||
self._attr_extra_state_attributes = {}
|
||||
|
||||
self._unique_id = unique_id
|
||||
@@ -288,6 +287,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
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.post_init(entry_infos)
|
||||
@@ -356,6 +359,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
_LOGGER.info("%s - The merged configuration is %s", self, entry_infos)
|
||||
|
||||
self._entry_infos = entry_infos
|
||||
|
||||
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)
|
||||
@@ -2649,18 +2654,68 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
"""Send an event"""
|
||||
send_vtherm_event(self._hass, event_type=event_type, entity=self, data=data)
|
||||
|
||||
def get_temperature_number_entities(self, config_entry: ConfigData):
|
||||
"""Creates all TemperatureNumber depending of the configuration of the Climate"""
|
||||
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()
|
||||
|
||||
# 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,
|
||||
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,
|
||||
)
|
||||
|
||||
return entity
|
||||
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")
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -76,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, entity.get_temperature_number_entities(entry)], True)
|
||||
async_add_entities([entity], True)
|
||||
|
||||
# Add services
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
@@ -23,7 +23,7 @@ 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 ensure_unique_string, slugify
|
||||
|
||||
from .vtherm_api import VersatileThermostatAPI
|
||||
from .commons import VersatileThermostatBaseEntity
|
||||
@@ -48,6 +48,9 @@ from .const import (
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -84,28 +87,45 @@ 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_ADD_CENTRAL_BOILER_CONTROL)
|
||||
|
||||
entities = []
|
||||
|
||||
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
|
||||
# )
|
||||
# )
|
||||
# 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
|
||||
)
|
||||
)
|
||||
|
||||
# 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
|
||||
# )
|
||||
# )
|
||||
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)
|
||||
@@ -113,27 +133,27 @@ async def async_setup_entry(
|
||||
for preset in CONF_PRESETS_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, False, False, entry.data
|
||||
hass, unique_id, name, 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
|
||||
hass, unique_id, name, preset, True, False, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
for preset in CONF_PRESETS_AWAY_VALUES:
|
||||
entities.append(
|
||||
CentralConfigTemperatureNumber(
|
||||
hass, unique_id, preset, preset, False, True, entry.data
|
||||
hass, unique_id, name, 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
|
||||
hass, unique_id, name, preset, True, True, entry.data
|
||||
)
|
||||
)
|
||||
|
||||
@@ -211,7 +231,6 @@ class CentralConfigTemperatureNumber(NumberEntity, RestoreEntity):
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
# _attr_translation_key = "temperature"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -227,7 +246,7 @@ class CentralConfigTemperatureNumber(NumberEntity, RestoreEntity):
|
||||
the restoration will do the trick."""
|
||||
|
||||
self._config_id = unique_id
|
||||
self._device_name = entry_infos.get(CONF_NAME)
|
||||
self._device_name = name
|
||||
# self._attr_name = name
|
||||
|
||||
self._attr_translation_key = preset_name
|
||||
@@ -236,7 +255,8 @@ class CentralConfigTemperatureNumber(NumberEntity, RestoreEntity):
|
||||
# "ac": "-AC" if is_ac else "",
|
||||
# "away": "-AWAY" if is_away else "",
|
||||
# }
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.central_configuration_{preset_name}"
|
||||
# self.entity_id = f"{NUMBER_DOMAIN}.central_configuration_{preset_name}"
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.{slugify(name)}_{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
|
||||
@@ -250,7 +270,7 @@ class CentralConfigTemperatureNumber(NumberEntity, RestoreEntity):
|
||||
# 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):
|
||||
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:
|
||||
@@ -346,7 +366,6 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
"""Representation of one temperature number"""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_translation_key = "temperature"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -360,40 +379,41 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
) -> 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))
|
||||
super().__init__(hass, unique_id, name)
|
||||
|
||||
split = name.split("_")
|
||||
# self._attr_name = split[0]
|
||||
# if "_" + split[1] == PRESET_AC_SUFFIX:
|
||||
# self._attr_name = self._attr_name + " AC"
|
||||
self._attr_translation_key = preset_name
|
||||
self.entity_id = f"{NUMBER_DOMAIN}.{slugify(name)}_{preset_name}"
|
||||
|
||||
# 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_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}_{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.
|
||||
# TODO see if this should be replace by the central config if any
|
||||
temp = None
|
||||
if temp := entry_infos.get(preset_name, 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
|
||||
|
||||
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]
|
||||
@@ -402,12 +422,16 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
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):
|
||||
if old_state is not None and ((value := float(old_state.state)) > 0):
|
||||
self._attr_value = self._attr_native_value = value
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -425,12 +449,6 @@ class TemperatureNumber( # pylint: disable=abstract-method
|
||||
)
|
||||
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"""
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 homeassistant.components.number import NumberEntity
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
|
||||
@@ -500,7 +500,12 @@ async def create_thermostat(
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
return search_entity(hass, entity_id, CLIMATE_DOMAIN)
|
||||
# We should reload the VTherm links
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api()
|
||||
entity = search_entity(hass, entity_id, CLIMATE_DOMAIN)
|
||||
if entity:
|
||||
await entity.init_presets(vtherm_api.find_central_configuration())
|
||||
return entity
|
||||
|
||||
|
||||
async def create_central_config( # pylint: disable=dangerous-default-value
|
||||
|
||||
@@ -14,7 +14,10 @@ from homeassistant.components.number import NumberEntity, DOMAIN as NUMBER_DOMAI
|
||||
|
||||
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
||||
|
||||
# from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
from custom_components.versatile_thermostat.thermostat_switch import (
|
||||
ThermostatOverSwitch,
|
||||
)
|
||||
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
|
||||
|
||||
from .commons import *
|
||||
@@ -394,3 +397,362 @@ async def test_add_number_for_central_config_without_temp_restore(
|
||||
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_over_switch_use_central(
|
||||
hass: HomeAssistant, skip_hass_states_is_state
|
||||
):
|
||||
"""Test the construction of a over switch vtherm with
|
||||
use central config for PRESET and PRESENCE.
|
||||
It also have old temp config value which should be not used.
|
||||
So it should have no Temp NumberEntity"""
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
vtherm_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchVTherm",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
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_USE_PRESENCE_CENTRAL_CONFIG: True,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
}
|
||||
| temps,
|
||||
)
|
||||
|
||||
# The restore should not be used
|
||||
with patch(
|
||||
"homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state",
|
||||
return_value=State(entity_id="number.mock_valve", state="23"),
|
||||
) as mock_restore_state:
|
||||
vtherm_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(vtherm_entry.entry_id)
|
||||
|
||||
assert mock_restore_state.call_count == 0
|
||||
|
||||
assert vtherm_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 is None
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=vtherm_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_over_switch_use_central_presence(
|
||||
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
|
||||
):
|
||||
"""Test the construction of a over switch vtherm with
|
||||
use central config for PRESET and PRESENCE.
|
||||
It also have old temp config value which should be not used.
|
||||
So it should have no Temp NumberEntity"""
|
||||
|
||||
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,
|
||||
}
|
||||
temps_missing = {
|
||||
"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,
|
||||
}
|
||||
|
||||
vtherm_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchVTherm",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
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_CYCLE_MIN: 5,
|
||||
CONF_HEATER: "switch.mock_switch1",
|
||||
CONF_USE_PRESENCE_FEATURE: True,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: True,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: False,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
}
|
||||
| temps
|
||||
| temps_missing,
|
||||
)
|
||||
|
||||
vtherm: BaseThermostat = await create_thermostat(
|
||||
hass, vtherm_entry, "climate.theoverswitchvtherm"
|
||||
)
|
||||
|
||||
# 1. We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.theoverswitchvtherm_" + 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=vtherm_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
|
||||
# 2. We search for NumberEntities to be missing
|
||||
for preset_name, value in temps_missing.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.theoverswitchvtherm_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity is None
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=vtherm_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val is None
|
||||
|
||||
# 3. The VTherm should be initialized with all presets and correct temperature
|
||||
assert vtherm
|
||||
assert isinstance(vtherm, ThermostatOverSwitch)
|
||||
assert vtherm.preset_modes == [
|
||||
PRESET_NONE,
|
||||
PRESET_FROST_PROTECTION,
|
||||
PRESET_ECO,
|
||||
PRESET_COMFORT,
|
||||
PRESET_BOOST,
|
||||
]
|
||||
|
||||
assert vtherm._presets == {
|
||||
PRESET_FROST_PROTECTION: temps["frost_temp"],
|
||||
PRESET_ECO: temps["eco_temp"],
|
||||
PRESET_COMFORT: temps["comfort_temp"],
|
||||
PRESET_BOOST: temps["boost_temp"],
|
||||
PRESET_ECO_AC: temps["eco_ac_temp"],
|
||||
PRESET_COMFORT_AC: temps["comfort_ac_temp"],
|
||||
PRESET_BOOST_AC: temps["boost_ac_temp"],
|
||||
}
|
||||
|
||||
# Preset away should be initialized with the central config
|
||||
assert vtherm._presets_away == {
|
||||
PRESET_FROST_PROTECTION
|
||||
+ PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["frost_away_temp"],
|
||||
PRESET_ECO + PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["eco_away_temp"],
|
||||
PRESET_COMFORT + PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["comfort_away_temp"],
|
||||
PRESET_BOOST + PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["boost_away_temp"],
|
||||
PRESET_ECO_AC + PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["eco_ac_away_temp"],
|
||||
PRESET_COMFORT_AC
|
||||
+ PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["comfort_ac_away_temp"],
|
||||
PRESET_BOOST_AC + PRESET_AWAY_SUFFIX: FULL_CENTRAL_CONFIG["boost_ac_away_temp"],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_add_number_for_over_switch_use_central_presets_and_restore(
|
||||
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
|
||||
):
|
||||
"""Test the construction of a over switch vtherm with
|
||||
use central config for PRESET and PRESENCE.
|
||||
It also have old temp config value which should be not used.
|
||||
So it should have no Temp NumberEntity"""
|
||||
|
||||
vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
temps = {
|
||||
"frost_away_temp": 23,
|
||||
"eco_away_temp": 23,
|
||||
"comfort_away_temp": 23,
|
||||
"boost_away_temp": 23,
|
||||
}
|
||||
temps_missing = {
|
||||
"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,
|
||||
"eco_ac_away_temp": 30.5,
|
||||
"comfort_ac_away_temp": 30.6,
|
||||
"boost_ac_away_temp": 30.7,
|
||||
}
|
||||
|
||||
vtherm_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheCentralConfigMockName",
|
||||
unique_id="centralConfigUniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverSwitchVTherm",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
|
||||
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_CYCLE_MIN: 5,
|
||||
CONF_HEATER: "switch.mock_switch1",
|
||||
CONF_USE_PRESENCE_FEATURE: True,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_WINDOW_CENTRAL_CONFIG: True,
|
||||
CONF_USE_POWER_CENTRAL_CONFIG: True,
|
||||
CONF_USE_MOTION_CENTRAL_CONFIG: True,
|
||||
}
|
||||
| temps
|
||||
| temps_missing,
|
||||
)
|
||||
|
||||
# The restore should not be used
|
||||
with patch(
|
||||
"homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state",
|
||||
return_value=State(entity_id="number.mock_valve", state="23"),
|
||||
) as mock_restore_state:
|
||||
vtherm: BaseThermostat = await create_thermostat(
|
||||
hass, vtherm_entry, "climate.theoverswitchvtherm"
|
||||
)
|
||||
|
||||
# We should try to restore all 4 temp entities
|
||||
assert mock_restore_state.call_count == 4
|
||||
|
||||
# 1. We search for NumberEntities
|
||||
for preset_name, value in temps.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.theoverswitchvtherm_" + 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=vtherm_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val == value
|
||||
|
||||
# 2. We search for NumberEntities to be missing
|
||||
for preset_name, value in temps_missing.items():
|
||||
temp_entity = search_entity(
|
||||
hass,
|
||||
"number.theoverswitchvtherm_" + preset_name,
|
||||
NUMBER_DOMAIN,
|
||||
)
|
||||
assert temp_entity is None
|
||||
|
||||
# Find temp Number into vtherm_api
|
||||
val = vtherm_api.get_temperature_number_value(
|
||||
config_id=vtherm_entry.entry_id, preset_name=preset_name
|
||||
)
|
||||
assert val is None
|
||||
|
||||
# 3. The VTherm should be initialized with all presets and correct temperature
|
||||
assert vtherm
|
||||
assert isinstance(vtherm, ThermostatOverSwitch)
|
||||
assert vtherm.preset_modes == [
|
||||
PRESET_NONE,
|
||||
PRESET_FROST_PROTECTION,
|
||||
PRESET_ECO,
|
||||
# PRESET_COMFORT, because temp is 0
|
||||
PRESET_BOOST,
|
||||
]
|
||||
|
||||
# Preset away should be empty cause we use central config for presets
|
||||
assert vtherm._presets == {
|
||||
PRESET_FROST_PROTECTION: FULL_CENTRAL_CONFIG["frost_temp"],
|
||||
PRESET_ECO: FULL_CENTRAL_CONFIG["eco_temp"],
|
||||
PRESET_COMFORT: FULL_CENTRAL_CONFIG["comfort_temp"],
|
||||
PRESET_BOOST: FULL_CENTRAL_CONFIG["boost_temp"],
|
||||
}
|
||||
|
||||
assert vtherm._presets_away == {
|
||||
PRESET_FROST_PROTECTION + PRESET_AWAY_SUFFIX: temps["frost_away_temp"],
|
||||
PRESET_ECO + PRESET_AWAY_SUFFIX: temps["eco_away_temp"],
|
||||
PRESET_COMFORT + PRESET_AWAY_SUFFIX: temps["comfort_away_temp"],
|
||||
PRESET_BOOST + PRESET_AWAY_SUFFIX: temps["boost_away_temp"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user