Files
versatile_thermostat/tests/test_central_mode.py
Jean-Marc Collin 9e52c843bc Issue-739-refactor-to-modularize (#742)
* Refactor Presence Feature

* Add PresenceFeatureManager ok

* Python 3.13

* Fix presence test

* Refactor power feature

* Add Motion manager. All tests ok

* Tests ok. But tests are not complete

* All tests Window Feature Manager ok.

* All windows tests ok

* Fix all testus with feature_window_manager ok

* Add test_auto_start_stop feature manager. All tests ok

* Add safety feature_safety_manager
Rename config attribute from security_ to safety_

* Documentation and release

* Add safety manager direct tests

* Typo

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2025-01-01 16:30:18 +01:00

1060 lines
40 KiB
Python

# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long, too-many-lines
""" 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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_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
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1,
},
)
# 1. set hvac_mode to COOL and preet ECO
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
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_change_central_mode_true_with_window(
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: True,
CONF_USE_MOTION_FEATURE: True,
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1,
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
CONF_WINDOW_DELAY: 0, # To be not obliged to wait
CONF_MOTION_SENSOR: "input_boolean.motion_sensor",
CONF_MOTION_DELAY: 10,
CONF_MOTION_OFF_DELAY: 30,
CONF_MOTION_PRESET: PRESET_BOOST,
CONF_NO_MOTION_PRESET: PRESET_ECO,
},
)
tz = get_tz(hass) # pylint: disable=invalid-name
event_timestamp = datetime.now(tz)
# 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_ACTIVITY)
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_UNKNOWN
# 2 Open the window
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = event_timestamp + timedelta(minutes=1)
try_function = await send_window_change_event(
entity, True, False, event_timestamp
)
await try_function(None)
assert mock_send_event.call_count == 1
mock_send_event.assert_has_calls(
[call.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.OFF})]
)
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_ON
# 3 Change central_mode to STOPPED
with patch("homeassistant.core.ServiceRegistry.async_call"):
event_timestamp = event_timestamp + timedelta(minutes=1)
entity._set_now(event_timestamp)
await select_entity.async_select_option(CENTRAL_MODE_STOPPED)
assert entity.last_central_mode is CENTRAL_MODE_STOPPED
# No change
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
# 4 Close the window
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = event_timestamp + timedelta(minutes=1)
try_function = await send_window_change_event(
entity, False, True, event_timestamp
)
await try_function(None)
# No hvac_mode change cause the central mode is STOPPED
assert mock_send_event.call_count == 0
# We should stay off because central is STOPPED
assert entity.hvac_mode == HVACMode.OFF
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_OFF
# 5 Back to Auto
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event:
event_timestamp = event_timestamp + timedelta(minutes=1)
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
assert mock_send_event.call_count == 1
mock_send_event.assert_has_calls(
[call.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.HEAT})]
)
# We should switch back to HEAT
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_switch_change_central_mode_true_with_cool_only_and_window(
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: True,
CONF_USE_MOTION_FEATURE: True,
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_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1,
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
CONF_WINDOW_DELAY: 0, # To be not obliged to wait
CONF_MOTION_SENSOR: "input_boolean.motion_sensor",
CONF_MOTION_DELAY: 10,
CONF_MOTION_OFF_DELAY: 30,
CONF_MOTION_PRESET: PRESET_BOOST,
CONF_NO_MOTION_PRESET: PRESET_ECO,
},
)
tz = get_tz(hass) # pylint: disable=invalid-name
event_timestamp = datetime.now(tz)
# 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_ACTIVITY)
assert entity.hvac_mode == HVACMode.HEAT
assert entity.preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_UNKNOWN
# 2 Change central_mode to COOL_ONLY
with patch("homeassistant.core.ServiceRegistry.async_call"):
event_timestamp = event_timestamp + timedelta(minutes=1)
entity._set_now(event_timestamp)
await select_entity.async_select_option(CENTRAL_MODE_COOL_ONLY)
assert entity.last_central_mode is CENTRAL_MODE_COOL_ONLY
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_off_reason == HVAC_OFF_REASON_MANUAL
await entity.async_set_preset_mode(PRESET_ACTIVITY)
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
# 3 Open the window
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = event_timestamp + timedelta(minutes=1)
try_function = await send_window_change_event(
entity, True, False, event_timestamp
)
await try_function(None)
# The VTherm is already off -> window detection is ignored
assert mock_send_event.call_count == 0
# mock_send_event.assert_has_calls(
# [call.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.OFF})]
# )
assert entity.hvac_mode == HVACMode.OFF
assert entity.hvac_off_reason == HVAC_OFF_REASON_MANUAL
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_ON
# 4 Change central_mode to AUTO
with patch("homeassistant.core.ServiceRegistry.async_call"):
event_timestamp = event_timestamp + timedelta(minutes=1)
entity._set_now(event_timestamp)
await select_entity.async_select_option(CENTRAL_MODE_AUTO)
assert entity.last_central_mode is CENTRAL_MODE_AUTO
# No change
assert entity.hvac_mode == HVACMode.OFF
# We have to a reason of WINDOW_DETECTION
assert entity.hvac_off_reason == HVAC_OFF_REASON_WINDOW_DETECTION
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
# 5 Close the window
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = event_timestamp + timedelta(minutes=1)
try_function = await send_window_change_event(
entity, False, True, event_timestamp
)
await try_function(None)
# hvac_mode change to HEAT
assert mock_send_event.call_count == 1
mock_send_event.assert_has_calls(
[call.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.HEAT})]
)
# We should stay off because central is STOPPED
assert entity.hvac_mode == HVACMode.HEAT
assert entity.hvac_off_reason is None
assert entity.preset_mode == PRESET_ACTIVITY
assert entity._saved_hvac_mode == HVACMode.HEAT
assert entity._saved_preset_mode == PRESET_ACTIVITY
assert entity.window_state is STATE_OFF