Add minimal_deactivation_delay (#905)

* Add minimal_deactivation_delay

In some cases users may want to keep the equipment on if it will deactivate for a short time.
This could help reduce wear on gas boilers.

* fix(prop_algorithm): correct the log message for minimal_deactivation_delay

* fix(translations): correct the translations for minimal_deactivation_delay
This commit is contained in:
Tomasz Madycki
2025-02-12 14:41:13 +01:00
committed by GitHub
parent 15a48105b0
commit bb4d3a59a9
38 changed files with 270 additions and 3 deletions

View File

@@ -38,6 +38,7 @@ async def test_tpi_calculation(
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_MINIMAL_DEACTIVATION_DELAY: 0,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
# CONF_DEVICE_POWER: 100,
@@ -159,6 +160,94 @@ async def test_tpi_calculation(
assert tpi_algo.off_time_sec == 60
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_minimal_deactivation_delay(
hass: HomeAssistant, skip_hass_states_is_state: None
): # pylint: disable=unused-argument
"""Test the minimal deactivation delay"""
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_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
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_MINIMAL_DEACTIVATION_DELAY: 60,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
},
)
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
assert entity._prop_algorithm # pylint: disable=protected-access
tpi_algo: PropAlgorithm = entity._prop_algorithm # pylint: disable=protected-access
assert tpi_algo
# off_time is less than minimal_deactivation_delay
tpi_algo.calculate(9, 6, 10, HVACMode.HEAT)
assert tpi_algo.on_percent == 0.89
assert tpi_algo.calculated_on_percent == 0.89
assert tpi_algo.on_time_sec == 300
assert tpi_algo.off_time_sec == 0
# off_time is less than minimal_deactivation_delay
tpi_algo.calculate(6, 0, 104, HVACMode.HEAT)
assert tpi_algo.on_percent == 0.82
assert tpi_algo.calculated_on_percent == 0.82
assert tpi_algo.on_time_sec == 300
assert tpi_algo.off_time_sec == 0
# off_time is exactly minimal_deactivation_delay
tpi_algo.calculate(10, 8, -10, HVACMode.HEAT)
assert tpi_algo.on_percent == 0.8
assert tpi_algo.calculated_on_percent == 0.8
assert tpi_algo.on_time_sec == 240
assert tpi_algo.off_time_sec == 60 # Equal to minimal_deactivation_delay
# off_time is greater than minimal_deactivation_delay
tpi_algo.calculate(10, 9, 0, HVACMode.HEAT)
assert tpi_algo.on_percent == 0.4
assert tpi_algo.calculated_on_percent == 0.4
assert tpi_algo.on_time_sec == 120
assert tpi_algo.off_time_sec == 180
# with safety mode
tpi_algo.set_safety(0.2)
tpi_algo.calculate(10, 8, -10, HVACMode.HEAT)
assert tpi_algo.on_percent == 0.2
assert tpi_algo.calculated_on_percent == 0.8
assert tpi_algo.on_time_sec == 60
assert tpi_algo.off_time_sec == 240
tpi_algo.unset_safety()
# with cooling mode
tpi_algo.calculate(10, 10, 90, HVACMode.COOL)
assert tpi_algo.on_percent == 0.8
assert tpi_algo.calculated_on_percent == 0.8
assert tpi_algo.on_time_sec == 240
assert tpi_algo.off_time_sec == 60
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_wrong_tpi_parameters(
@@ -174,6 +263,7 @@ async def test_wrong_tpi_parameters(
0.01,
5,
1,
1,
"entity_id",
)
# We should not be there
@@ -190,6 +280,7 @@ async def test_wrong_tpi_parameters(
0,
2,
3,
3,
"entity_id",
)
# We should not be there
@@ -206,6 +297,7 @@ async def test_wrong_tpi_parameters(
0,
2,
3,
3,
"entity_id",
)
# We should not be there
@@ -222,6 +314,7 @@ async def test_wrong_tpi_parameters(
None,
2,
3,
3,
"entity_id",
)
# We should not be there
@@ -238,6 +331,7 @@ async def test_wrong_tpi_parameters(
0.00001,
None,
3,
3,
"entity_id",
)
# We should not be there
@@ -262,6 +356,23 @@ async def test_wrong_tpi_parameters(
# the normal case
pass
# Test minimal_activation_delay
try:
algo = PropAlgorithm(
PROPORTIONAL_FUNCTION_TPI,
0.6,
0.00001,
0,
3,
None,
"entity_id",
)
# We should not be there
assert False
except TypeError as e:
# the normal case
pass
# Test vtherm_entity_id
try:
algo = PropAlgorithm(
@@ -270,6 +381,7 @@ async def test_wrong_tpi_parameters(
0.00001,
0,
12,
12,
None,
)
# We should not be there