Compare commits

...

1 Commits

Author SHA1 Message Date
Jean-Marc Collin
a17aba45fa Normal algo working and testu ok 2024-01-03 09:57:18 +00:00
16 changed files with 1035 additions and 13 deletions

View File

@@ -116,6 +116,11 @@ from .const import (
ATTR_TOTAL_ENERGY,
PRESET_AC_SUFFIX,
DEFAULT_SHORT_EMA_PARAMS,
CENTRAL_MODE_AUTO,
CENTRAL_MODE_STOPPED,
CENTRAL_MODE_HEAT_ONLY,
CENTRAL_MODE_COOL_ONLY,
CENTRAL_MODE_FROST_PROTECTION,
)
from .config_schema import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -158,6 +163,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
frozenset(
{
"is_on",
"is_controlled_by_central_mode",
"last_central_mode",
"type",
"frost_temp",
"eco_temp",
@@ -273,6 +280,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._now = None
self._attr_fan_mode = None
self._is_central_mode = None
self._last_central_mode = None
self.post_init(entry_infos)
def clean_central_config_doublon(self, config_entry, central_config) -> dict:
@@ -434,6 +444,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._presence_on = self._presence_sensor_entity_id is not None
if self._ac_mode:
# Added by https://github.com/jmcollin78/versatile_thermostat/pull/144
# Some over_switch can do both heating and cooling
self._hvac_list = [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF]
else:
self._hvac_list = [HVACMode.HEAT, HVACMode.OFF]
@@ -552,6 +564,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
short_ema_params.get("max_alpha"),
)
self._is_central_mode = not (
entry_infos.get(CONF_USE_CENTRAL_MODE) is False
) # Default value (None) is True
_LOGGER.debug(
"%s - Creation of a new VersatileThermostat entity: unique_id=%s",
self,
@@ -1130,6 +1146,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"""True if the VTherm is on (! HVAC_OFF)"""
return self.hvac_mode and self.hvac_mode != HVACMode.OFF
@property
def is_controlled_by_central_mode(self) -> bool:
"""Returns True if this VTherm can be controlled by the central_mode"""
return self._is_central_mode
@property
def last_central_mode(self) -> str | None:
"""Returns the last central_mode taken into account.
Is None if the VTherm is not controlled by central_mode"""
return self._last_central_mode
def underlying_entity_id(self, index=0) -> str | None:
"""The climate_entity_id. Added for retrocompatibility reason"""
if index < self.nb_underlying_entities:
@@ -1177,11 +1204,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
)
# If AC is on maybe we have to change the temperature in force mode, but not in frost mode (there is no Frost protection possible in AC mode)
if self._ac_mode:
if self._hvac_mode == HVACMode.COOL:
if self.preset_mode != PRESET_FROST_PROTECTION:
await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
else:
await self._async_set_preset_mode_internal(PRESET_ECO, True)
await self._async_set_preset_mode_internal(PRESET_ECO, True, False)
if need_control_heating and sub_need_control_heating:
await self.async_control_heating(force=True)
@@ -1195,12 +1222,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.async_write_ha_state()
self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode})
async def async_set_preset_mode(self, preset_mode):
@overrides
async def async_set_preset_mode(self, preset_mode, overwrite_saved_preset=True):
"""Set new preset mode."""
await self._async_set_preset_mode_internal(preset_mode)
await self._async_set_preset_mode_internal(
preset_mode, force=False, overwrite_saved_preset=overwrite_saved_preset
)
await self.async_control_heating(force=True)
async def _async_set_preset_mode_internal(self, preset_mode, force=False):
async def _async_set_preset_mode_internal(
self, preset_mode, force=False, overwrite_saved_preset=True
):
"""Set new preset mode."""
_LOGGER.info("%s - Set preset_mode: %s force=%s", self, preset_mode, force)
if (
@@ -1242,7 +1274,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.reset_last_temperature_time(old_preset_mode)
self.save_preset_mode()
if overwrite_saved_preset:
self.save_preset_mode()
self.recalculate()
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
@@ -1998,6 +2031,66 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
return self._overpowering_state
async def check_central_mode(self, new_central_mode, old_central_mode) -> None:
"""Take into account a central mode change"""
if not self.is_controlled_by_central_mode:
self._last_central_mode = None
return
_LOGGER.info(
"%s - Central mode have change from %s to %s",
self,
old_central_mode,
new_central_mode,
)
self._last_central_mode = new_central_mode
def save_all():
"""save preset and hvac_mode"""
self.save_preset_mode()
self.save_hvac_mode()
if new_central_mode == CENTRAL_MODE_AUTO:
await self.restore_hvac_mode()
await self.restore_preset_mode()
return
if old_central_mode == CENTRAL_MODE_AUTO:
save_all()
if new_central_mode == CENTRAL_MODE_STOPPED:
await self.async_set_hvac_mode(HVACMode.OFF)
return
if new_central_mode == CENTRAL_MODE_COOL_ONLY:
if HVACMode.COOL in self.hvac_modes:
await self.async_set_hvac_mode(HVACMode.COOL)
else:
await self.async_set_hvac_mode(HVACMode.OFF)
return
if new_central_mode == CENTRAL_MODE_HEAT_ONLY:
if HVACMode.HEAT in self.hvac_modes:
await self.async_set_hvac_mode(HVACMode.HEAT)
else:
await self.async_set_hvac_mode(HVACMode.OFF)
return
if new_central_mode == CENTRAL_MODE_FROST_PROTECTION:
if (
PRESET_FROST_PROTECTION in self.preset_modes
and HVACMode.HEAT in self.hvac_modes
):
await self.async_set_hvac_mode(HVACMode.HEAT)
await self.async_set_preset_mode(
PRESET_FROST_PROTECTION, overwrite_saved_preset=False
)
else:
await self.async_set_hvac_mode(HVACMode.OFF)
return
def _set_now(self, now: datetime):
"""Set the now timestamp. This is only for tests purpose"""
self._now = now
@@ -2239,6 +2332,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"hvac_mode": self.hvac_mode,
"preset_mode": self.preset_mode,
"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],

View File

@@ -42,6 +42,7 @@ STEP_MAIN_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name
),
vol.Required(CONF_CYCLE_MIN, default=5): cv.positive_int,
vol.Optional(CONF_DEVICE_POWER, default="1"): vol.Coerce(float),
vol.Optional(CONF_USE_CENTRAL_MODE, default=True): cv.boolean,
vol.Optional(CONF_USE_WINDOW_FEATURE, default=False): cv.boolean,
vol.Optional(CONF_USE_MOTION_FEATURE, default=False): cv.boolean,
vol.Optional(CONF_USE_POWER_FEATURE, default=False): cv.boolean,

View File

@@ -35,7 +35,12 @@ HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY]
DOMAIN = "versatile_thermostat"
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.BINARY_SENSOR, Platform.SENSOR]
PLATFORMS: list[Platform] = [
Platform.CLIMATE,
Platform.BINARY_SENSOR,
Platform.SENSOR,
Platform.SELECT,
]
CONF_HEATER = "heater_entity_id"
CONF_HEATER_2 = "heater_entity2_id"
@@ -113,6 +118,8 @@ CONF_USE_PRESENCE_CENTRAL_CONFIG = "use_presence_central_config"
CONF_USE_PRESETS_CENTRAL_CONFIG = "use_presets_central_config"
CONF_USE_ADVANCED_CENTRAL_CONFIG = "use_advanced_central_config"
CONF_USE_CENTRAL_MODE = "use_central_mode"
DEFAULT_SHORT_EMA_PARAMS = {
"max_alpha": 0.5,
# In sec
@@ -242,6 +249,7 @@ ALL_CONF = (
CONF_USE_POWER_CENTRAL_CONFIG,
CONF_USE_PRESENCE_CENTRAL_CONFIG,
CONF_USE_ADVANCED_CENTRAL_CONFIG,
CONF_USE_CENTRAL_MODE,
]
+ CONF_PRESETS_VALUES
+ CONF_PRESETS_AWAY_VALUES
@@ -297,6 +305,19 @@ AUTO_FAN_DEACTIVATED_MODES = ["mute", "auto", "low"]
CENTRAL_CONFIG_NAME = "Central configuration"
CENTRAL_MODE_AUTO = "Auto"
CENTRAL_MODE_STOPPED = "Stopped"
CENTRAL_MODE_HEAT_ONLY = "Heat only"
CENTRAL_MODE_COOL_ONLY = "Cool only"
CENTRAL_MODE_FROST_PROTECTION = "Frost protection"
CENTRAL_MODES = [
CENTRAL_MODE_AUTO,
CENTRAL_MODE_STOPPED,
CENTRAL_MODE_HEAT_ONLY,
CENTRAL_MODE_COOL_ONLY,
CENTRAL_MODE_FROST_PROTECTION,
]
# A special regulation parameter suggested by @Maia here: https://github.com/jmcollin78/versatile_thermostat/discussions/154
class RegulationParamSlow:

View File

@@ -0,0 +1,134 @@
# 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.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
from homeassistant.components.select import SelectEntity
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.helpers.entity_component import EntityComponent
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from .const import (
DOMAIN,
DEVICE_MANUFACTURER,
CONF_NAME,
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
CENTRAL_MODE_AUTO,
CENTRAL_MODES,
overrides,
)
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the VersatileThermostat selects with config flow."""
_LOGGER.debug(
"Calling async_setup_entry entry=%s, data=%s", entry.entry_id, entry.data
)
unique_id = entry.entry_id
name = entry.data.get(CONF_NAME)
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG:
return
entities = [
CentralModeSelect(hass, unique_id, name, entry.data),
]
async_add_entities(entities, True)
class CentralModeSelect(SelectEntity, RestoreEntity):
"""Representation of a Energy sensor which exposes the energy"""
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
"""Initialize the energy sensor"""
self._config_id = unique_id
self._device_name = entry_infos.get(CONF_NAME)
self._attr_name = "Central Mode"
self._attr_unique_id = "central_mode"
self._attr_options = CENTRAL_MODES
self._attr_current_option = CENTRAL_MODE_AUTO
@property
def icon(self) -> str | None:
return "mdi:form-select"
@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()
old_state = await self.async_get_last_state()
_LOGGER.debug(
"%s - Calling async_added_to_hass old_state is %s", self, old_state
)
if old_state is not None:
self.async_select_option = old_state.state
@callback
async def _async_startup_internal(*_):
_LOGGER.debug("%s - Calling async_startup_internal", self)
await self.notify_central_mode_change()
if self.hass.state == CoreState.running:
await _async_startup_internal()
else:
self.hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_START, _async_startup_internal
)
@overrides
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
old_option = self._attr_current_option
if option == old_option:
return
if option in CENTRAL_MODES:
self._attr_current_option = option
await self.notify_central_mode_change(old_central_mode=old_option)
async def notify_central_mode_change(self, old_central_mode=None):
"""Notify all VTherm that the central_mode have change"""
# Update all VTherm states
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
for entity in component.entities:
if isinstance(entity, BaseThermostat):
_LOGGER.debug(
"Changing the central_mode. We have find %s to update",
entity.name,
)
await entity.check_central_mode(
self._attr_current_option, old_central_mode
)
def __str__(self):
return f"VersatileThermostat-{self.name}"

View File

@@ -24,6 +24,7 @@
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"device_power": "Device power",
"use_central_mode": "Enable the control by central mode ('central_mode')",
"use_window_feature": "Use window detection",
"use_motion_feature": "Use motion detection",
"use_power_feature": "Use power management",
@@ -31,6 +32,7 @@
"use_main_central_config": "Use central main configuration"
},
"data_description": {
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
}
@@ -254,6 +256,7 @@
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"device_power": "Device power",
"use_central_mode": "Enable the control by central mode ('central_mode')",
"use_window_feature": "Use window detection",
"use_motion_feature": "Use motion detection",
"use_power_feature": "Use power management",
@@ -261,6 +264,7 @@
"use_main_central_config": "Use central main configuration"
},
"data_description": {
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific configuration for this VTherm",
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
}

View File

@@ -24,6 +24,7 @@
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"device_power": "Device power",
"use_central_mode": "Enable the control by central mode ('central_mode')",
"use_window_feature": "Use window detection",
"use_motion_feature": "Use motion detection",
"use_power_feature": "Use power management",
@@ -31,6 +32,7 @@
"use_main_central_config": "Use central main configuration"
},
"data_description": {
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific main configuration for this VTherm",
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
}
@@ -254,6 +256,7 @@
"temp_min": "Minimal temperature allowed",
"temp_max": "Maximal temperature allowed",
"device_power": "Device power",
"use_central_mode": "Enable the control by central mode ('central_mode')",
"use_window_feature": "Use window detection",
"use_motion_feature": "Use motion detection",
"use_power_feature": "Use power management",
@@ -261,6 +264,7 @@
"use_main_central_config": "Use central main configuration"
},
"data_description": {
"use_central_mode": "Check to enable the control of the VTherm with the select central_mode entities",
"use_main_central_config": "Check to use the central main configuration. Uncheck to use a specific configuration for this VTherm",
"external_temperature_sensor_entity_id": "Outdoor temperature sensor entity id. Not used if central configuration is selected"
}

View File

@@ -24,6 +24,7 @@
"temp_min": "Température minimale permise",
"temp_max": "Température maximale permise",
"device_power": "Puissance de l'équipement",
"use_central_mode": "Autoriser le controle par le mode central ('central_mode`)",
"use_window_feature": "Avec détection des ouvertures",
"use_motion_feature": "Avec détection de mouvement",
"use_power_feature": "Avec gestion de la puissance",
@@ -31,6 +32,7 @@
"use_main_central_config": "Utiliser la configuration centrale principale"
},
"data_description": {
"use_central_mode": "Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale",
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure. N'est pas utilisé si la configuration centrale est utilisée",
"use_main_central_config": "Cochez pour utiliser la configuration centrale principale. Décochez et saisissez les attributs pour utiliser une configuration spécifique principale"
}
@@ -254,6 +256,7 @@
"temp_min": "Température minimale permise",
"temp_max": "Température maximale permise",
"device_power": "Puissance de l'équipement",
"use_central_mode": "Autoriser le controle par le mode central ('central_mode`)",
"use_window_feature": "Avec détection des ouvertures",
"use_motion_feature": "Avec détection de mouvement",
"use_power_feature": "Avec gestion de la puissance",
@@ -261,6 +264,7 @@
"use_main_central_config": "Utiliser la configuration centrale"
},
"data_description": {
"use_central_mode": "Cochez pour autoriser le contrôle du VTherm par la liste déroulante 'central_mode' de l'entité configuration centrale",
"use_main_central_config": "Cochez pour utiliser la configuration centrale. Décochez et saisissez les attributs pour utiliser une configuration spécifique",
"external_temperature_sensor_entity_id": "Entity id du capteur de température extérieure. N'est pas utilisé si la configuration centrale est utilisée"
}

View File

@@ -790,6 +790,7 @@ class UnderlyingValve(UnderlyingEntity):
):
"""We use this function to change the on_percent"""
if force:
self._percent_open = self.cap_sent_value(self._percent_open)
await self.send_percent_open()
@overrides

View File

@@ -20,7 +20,6 @@ class VersatileThermostatAPI(dict):
"""The VersatileThermostatAPI"""
_hass: HomeAssistant = None
# _entries: Dict(str, ConfigEntry)
@classmethod
def get_vtherm_api(cls, hass=None):
@@ -64,14 +63,12 @@ class VersatileThermostatAPI(dict):
def add_entry(self, entry: ConfigEntry):
"""Add a new entry"""
_LOGGER.debug("Add the entry %s", entry.entry_id)
# self._entries[entry.entry_id] = entry
# Add the entry in hass.data
VersatileThermostatAPI._hass.data[DOMAIN][entry.entry_id] = entry
def remove_entry(self, entry: ConfigEntry):
"""Remove an entry"""
_LOGGER.debug("Remove the entry %s", entry.entry_id)
# self._entries.pop(entry.entry_id)
VersatileThermostatAPI._hass.data[DOMAIN].pop(entry.entry_id)
# If not more entries are preset, remove the API
if len(self) == 0:

7
pyrightconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"include": [
"custom_components/versatile_thermostat/**",
"homeassistant/**"
],
"reportShadowedImports": false
}

View File

@@ -185,6 +185,7 @@ class MockClimate(ClimateEntity):
hvac_mode: HVACMode = HVACMode.OFF,
hvac_action: HVACAction = HVACAction.OFF,
fan_modes: list[str] = None,
hvac_modes: list[str] = None,
) -> None:
"""Initialize the thermostat."""
@@ -200,7 +201,11 @@ class MockClimate(ClimateEntity):
HVACAction.OFF if hvac_mode == HVACMode.OFF else HVACAction.HEATING
)
self._attr_hvac_mode = hvac_mode
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
self._attr_hvac_modes = (
hvac_modes
if hvac_modes is not None
else [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
)
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_target_temperature = 20
self._attr_current_temperature = 15

View File

@@ -50,7 +50,8 @@ MOCK_TH_OVER_CLIMATE_MAIN_CONFIG = {
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_DEVICE_POWER: 1,
CONF_USE_MAIN_CENTRAL_CONFIG: False
CONF_USE_MAIN_CENTRAL_CONFIG: False,
CONF_USE_CENTRAL_MODE: True
# Keep default values which are False
}

734
tests/test_central_mode.py Normal file
View File

@@ -0,0 +1,734 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Test the central_configuration """
from unittest.mock import patch # , call
# from datetime import datetime # , timedelta
from homeassistant.core import HomeAssistant
from homeassistant.components.climate import HVACMode
# from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.select import DOMAIN as SELECT_DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.thermostat_switch import (
ThermostatOverSwitch,
)
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
# @pytest.mark.parametrize("expected_lingering_tasks", [True])
# @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_config_with_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state
):
"""A config with central_mode True"""
# Add a Switch VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverSwitchMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: True,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_HEATER: "switch.mock_switch",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
assert entity.name == "TheOverSwitchMockName"
assert entity.is_over_switch
assert entity.is_controlled_by_central_mode
assert entity.last_central_mode is None # cause no central config exists
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_config_with_central_mode_false(
hass: HomeAssistant, skip_hass_states_is_state
):
"""A config with central_mode False"""
# Add a Climate VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: False,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate
assert entity.is_controlled_by_central_mode is False
assert entity.last_central_mode is None # cause no central config exists
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_config_with_central_mode_none(
hass: HomeAssistant, skip_hass_states_is_state
):
"""A config with central_mode is None"""
# Add a Switch VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverValveMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverValveMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_VALVE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: True,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_VALVE: "number.mock_valve",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theovervalvemockname"
)
assert entity
assert entity.name == "TheOverValveMockName"
assert entity.is_over_valve
assert entity.is_controlled_by_central_mode
assert entity.last_central_mode is None # cause no central config exists
async def test_switch_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
):
"""test that changes with over_switch config with central_mode True are
taken into account"""
# Add a Switch VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverSwitchMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: True,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_HEATER: "switch.mock_switch",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
# 1 initialize entity and find select entity
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
assert entity.is_controlled_by_central_mode
assert entity.last_central_mode is None
# Find the select entity
select_entity = search_entity(hass, "select.central_mode", SELECT_DOMAIN)
assert select_entity
assert select_entity.current_option == CENTRAL_MODE_AUTO
assert select_entity.options == CENTRAL_MODES
# start entity
await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_BOOST
# 2 change central_mode to STOPPED
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_STOPPED)
assert entity.last_central_mode is CENTRAL_MODE_STOPPED
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_BOOST
# 3 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode == PRESET_COMFORT
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored as before the STOP and preset should be restored with the last choosen preset (COMFORT here)
assert entity.last_central_mode is CENTRAL_MODE_AUTO
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 4 change central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
# hvac_mode should be set to OFF because there is no COOL mode for this VTherm
assert entity.last_central_mode is CENTRAL_MODE_COOL_ONLY
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_COMFORT
# 5 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to HEAT
assert entity.last_central_mode is CENTRAL_MODE_AUTO
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 6 change central_mode to HEAT_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_HEAT_ONLY)
# hvac_mode should stay in HEAT mode
assert entity.last_central_mode is CENTRAL_MODE_HEAT_ONLY
assert entity.hvac_mode == HVACMode.HEAT
# No change
assert entity.preset_mode == PRESET_COMFORT
# 7 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to HEAT
assert entity.last_central_mode is CENTRAL_MODE_AUTO
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 8 change central_mode to FROST_PROTECTION
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_FROST_PROTECTION)
# hvac_mode should stay in HEAT mode
assert entity.last_central_mode is CENTRAL_MODE_FROST_PROTECTION
assert entity.hvac_mode == HVACMode.HEAT
# change to Frost
assert entity.preset_mode == PRESET_FROST_PROTECTION
# 9 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to HEAT
assert entity.last_central_mode is CENTRAL_MODE_AUTO
assert entity.hvac_mode == HVACMode.HEAT
# preset restored to COMFORT
assert entity.preset_mode == PRESET_COMFORT
async def test_switch_ac_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
):
"""test that changes with over_switch config with central_mode True are
taken into account"""
# Add a Switch VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverSwitchMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: True,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_HEATER: "switch.mock_switch",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
CONF_AC_MODE: True,
},
)
# 1 initialize entity and find select entity
with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
assert entity.is_controlled_by_central_mode
assert entity.ac_mode is True
assert entity.hvac_modes == [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF]
# Find the select entity
select_entity = search_entity(hass, "select.central_mode", SELECT_DOMAIN)
assert select_entity
assert select_entity.current_option == CENTRAL_MODE_AUTO
assert select_entity.options == CENTRAL_MODES
# start entity in cooling mode
await entity.async_set_hvac_mode(HVACMode.COOL)
await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_BOOST
# 2 change central_mode to STOPPED
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_STOPPED)
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_BOOST
# 3 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode == PRESET_COMFORT
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored as before the STOP and preset should be restored with the last choosen preset (COMFORT here)
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 4 change central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
# hvac_mode should be set to OFF because there is no COOL mode for this VTherm
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 5 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to HEAT
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 6 change central_mode to HEAT_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_HEAT_ONLY)
# hvac_mode should stay in HEAT mode
assert entity.hvac_mode == HVACMode.HEAT
# No change
assert entity.preset_mode == PRESET_COMFORT
# 7 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to COOL
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 8 change central_mode to FROST_PROTECTION
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_FROST_PROTECTION)
# hvac_mode should stay in COOL mode
assert entity.hvac_mode == HVACMode.HEAT
# change to Frost
assert entity.preset_mode == PRESET_FROST_PROTECTION
# 9 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# hvac_mode should be restored to COOL
assert entity.hvac_mode == HVACMode.COOL
# preset restored to COMFORT
assert entity.preset_mode == PRESET_COMFORT
async def test_climate_ac_change_central_mode_false(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
):
"""test that changes with over_climate config with central_mode False are
not taken into account"""
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
# Add a Climate VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: False,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
with patch("homeassistant.core.ServiceRegistry.async_call"), patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate
assert entity.is_controlled_by_central_mode is False
assert entity.hvac_modes == [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
# Find the select entity
select_entity = search_entity(hass, "select.central_mode", SELECT_DOMAIN)
assert select_entity
assert select_entity.current_option == CENTRAL_MODE_AUTO
assert select_entity.options == CENTRAL_MODES
# start entity in Heating mode
await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_BOOST
# 2 change central_mode to STOPPED
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_STOPPED)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_BOOST
# 3 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode == PRESET_COMFORT
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 4 change central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 5 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 6 change central_mode to HEAT_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_HEAT_ONLY)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 7 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 8 change central_mode to FROST_PROTECTION
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_FROST_PROTECTION)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
# 9 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_COMFORT
async def test_climate_ac_only_change_central_mode_true(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config
):
"""test that changes with over_climate with AC only config with central_mode True are
taken into account
Test also switching from central_mode without coming to AUTO each time"""
fake_underlying_climate = MockClimate(
hass,
"mockUniqueId",
"MockClimateName",
entry_infos={},
hvac_modes=[HVACMode.OFF, HVACMode.COOL],
)
# Add a Climate VTherm
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverClimateMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverClimateMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_USE_CENTRAL_MODE: True,
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_CLIMATE: "climate.mock_climate",
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
},
)
with patch("homeassistant.core.ServiceRegistry.async_call"), patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
return_value=fake_underlying_climate,
):
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity.is_over_climate
assert entity.is_controlled_by_central_mode is True
assert entity.hvac_modes == [HVACMode.OFF, HVACMode.COOL]
# Find the select entity
select_entity = search_entity(hass, "select.central_mode", SELECT_DOMAIN)
assert select_entity
assert select_entity.current_option == CENTRAL_MODE_AUTO
assert select_entity.options == CENTRAL_MODES
# start entity in Cooling mode
await entity.async_set_hvac_mode(HVACMode.COOL)
await entity.async_set_preset_mode(PRESET_ECO)
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_ECO
# 2 change central_mode to STOPPED
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_STOPPED)
# No change
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_ECO
# 3 change central_mode to HEAT ONLY after switching to COMFORT preset
with patch("homeassistant.core.ServiceRegistry.async_call"):
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode == PRESET_COMFORT
await select_entity.async_select_option(CENTRAL_MODE_HEAT_ONLY)
# Stay in OFF because HEAT is not permitted
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_COMFORT
# 4 change central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
# switch back to COOL restoring the preset
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 5 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 6 change central_mode to FROST_PROTECTION
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_FROST_PROTECTION)
# No change
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_COMFORT
# 7 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
# 8 change central_mode to FROST_PROTECTION
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_FROST_PROTECTION)
# No change
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_COMFORT
# 9 change back central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
# No change
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_COMFORT
await entity.async_set_preset_mode(PRESET_ECO)
# 10 change back central_mode to HEAT_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_HEAT_ONLY)
# Shutdown cause no HEAT
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_ECO
# 11 change back central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
# No change
assert entity.hvac_mode == HVACMode.COOL
assert entity.preset_mode == PRESET_ECO

View File

@@ -363,6 +363,7 @@ async def test_user_config_flow_window_auto_ok(
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_WINDOW_DELAY: 30, # the default value is added
CONF_USE_CENTRAL_MODE: True, # True is the defaulf value
} | MOCK_TH_OVER_SWITCH_TYPE_CONFIG | MOCK_TH_OVER_SWITCH_TPI_CONFIG | MOCK_WINDOW_AUTO_CONFIG | {
CONF_USE_MAIN_CENTRAL_CONFIG: True,
CONF_USE_TPI_CENTRAL_CONFIG: False,
@@ -510,6 +511,7 @@ async def test_user_config_flow_over_4_switches(
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_USE_MAIN_CENTRAL_CONFIG: True,
CONF_USE_CENTRAL_MODE: False,
}
TYPE_CONFIG = { # pylint: disable=wildcard-import, invalid-name

View File

@@ -129,7 +129,7 @@ async def test_over_switch_ac_full_start(
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_action is HVACAction.OFF
assert entity.target_temperature == 16 # eco_ac_away
assert entity.target_temperature == 27 # eco_ac_away (no change)
# Close a window
with patch("homeassistant.helpers.condition.state", return_value=True):

View File

@@ -283,6 +283,18 @@ async def test_over_valve_full_start(
assert entity.is_device_active is True
assert entity.hvac_action == HVACAction.HEATING
# Test window open/close (with a normal min/max so that is_device_active is False when open_percent is 0)
expected_state = State(
entity_id="number.mock_valve", state="0", attributes={"min": 0, "max": 99}
)
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"homeassistant.core.StateMachine.get", return_value=expected_state
):
# Open a window
with patch("homeassistant.helpers.condition.state", return_value=True):
event_timestamp = now - timedelta(minutes=1)