All tests ok

This commit is contained in:
Jean-Marc Collin
2025-01-04 09:39:42 +00:00
parent 24fcb7a161
commit 0b5d937968
6 changed files with 231 additions and 136 deletions

View File

@@ -1111,3 +1111,9 @@ class SideEffects:
def add_or_update_side_effect(self, key: str, new_value: Any): def add_or_update_side_effect(self, key: str, new_value: Any):
"""Update the value of a side effect""" """Update the value of a side effect"""
self._current_side_effects[key] = new_value self._current_side_effects[key] = new_value
async def do_central_power_refresh(hass):
"""Do a central power refresh"""
await VersatileThermostatAPI.get_vtherm_api().central_power_manager.refresh_state()
return hass.async_block_till_done()

View File

@@ -1,4 +1,4 @@
# pylint: disable=wildcard-import, unused-wildcard-import, unused-argument, line-too-long # pylint: disable=wildcard-import, unused-wildcard-import, unused-argument, line-too-long, protected-access
""" Test the normal start of a Thermostat """ """ Test the normal start of a Thermostat """
from unittest.mock import patch from unittest.mock import patch
@@ -107,9 +107,16 @@ async def test_overpowering_binary_sensors(
skip_hass_states_is_state, skip_hass_states_is_state,
skip_turn_on_off_heater, skip_turn_on_off_heater,
skip_send_event, skip_send_event,
init_central_power_manager,
): ):
"""Test the overpowering binary sensors in thermostat type""" """Test the overpowering binary sensors in thermostat type"""
temps = {
"eco": 17,
"comfort": 18,
"boost": 19,
}
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
title="TheOverSwitchMockName", title="TheOverSwitchMockName",
@@ -122,9 +129,6 @@ async def test_overpowering_binary_sensors(
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15, CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False, CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False, CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True, CONF_USE_POWER_FEATURE: True,
@@ -136,15 +140,13 @@ async def test_overpowering_binary_sensors(
CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100, CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12, CONF_PRESET_POWER: 12,
}, },
) )
entity: BaseThermostat = await create_thermostat( entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname" hass, entry, "climate.theoverswitchmockname", temps
) )
assert entity assert entity
@@ -153,7 +155,8 @@ async def test_overpowering_binary_sensors(
) )
assert overpowering_binary_sensor assert overpowering_binary_sensor
now: datetime = datetime.now(tz=get_tz(hass)) now: datetime = NowClass.get_now(hass)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# Overpowering should be not set because poer have not been received # Overpowering should be not set because poer have not been received
await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_preset_mode(PRESET_COMFORT)
@@ -166,22 +169,40 @@ async def test_overpowering_binary_sensors(
assert overpowering_binary_sensor.state is STATE_OFF assert overpowering_binary_sensor.state is STATE_OFF
assert overpowering_binary_sensor.device_class == BinarySensorDeviceClass.POWER assert overpowering_binary_sensor.device_class == BinarySensorDeviceClass.POWER
await send_power_change_event(entity, 100, now) # Send power mesurement
await send_max_power_change_event(entity, 150, now) side_effects = SideEffects(
assert entity.power_manager.is_overpowering_detected is True {
assert entity.power_manager.overpowering_state is STATE_ON "sensor.the_power_sensor": State("sensor.the_power_sensor", 100),
"sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 150),
},
State("unknown.entity_id", "unknown"),
)
# fmt:off
with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
# fmt: on
await send_power_change_event(entity, 100, now)
await send_max_power_change_event(entity, 150, now)
# Simulate the event reception assert entity.power_manager.is_overpowering_detected is True
await overpowering_binary_sensor.async_my_climate_changed() assert entity.power_manager.overpowering_state is STATE_ON
assert overpowering_binary_sensor.state == STATE_ON
# Simulate the event reception
await overpowering_binary_sensor.async_my_climate_changed()
assert overpowering_binary_sensor.state == STATE_ON
# set max power to a low value # set max power to a low value
await send_max_power_change_event(entity, 201, now) side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 201))
assert entity.power_manager.is_overpowering_detected is False # fmt:off
assert entity.power_manager.overpowering_state is STATE_OFF with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
# Simulate the event reception # fmt: on
await overpowering_binary_sensor.async_my_climate_changed() now = now + timedelta(seconds=61)
assert overpowering_binary_sensor.state == STATE_OFF VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_max_power_change_event(entity, 201, now)
assert entity.power_manager.is_overpowering_detected is False
assert entity.power_manager.overpowering_state is STATE_OFF
# Simulate the event reception
await overpowering_binary_sensor.async_my_climate_changed()
assert overpowering_binary_sensor.state == STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])

View File

@@ -266,7 +266,9 @@ async def test_bug_272(
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state): async def test_bug_407(
hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Test the followin case in power management: """Test the followin case in power management:
1. a heater is active (heating). So the power consumption takes the heater power into account. We suppose the power consumption is near the threshold, 1. a heater is active (heating). So the power consumption takes the heater power into account. We suppose the power consumption is near the threshold,
2. the user switch preset let's say from Comfort to Boost, 2. the user switch preset let's say from Comfort to Boost,
@@ -275,6 +277,12 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
""" """
temps = {
"eco": 17,
"comfort": 18,
"boost": 19,
}
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
title="TheOverSwitchMockName", title="TheOverSwitchMockName",
@@ -287,9 +295,6 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_CYCLE_MIN: 5, CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15, CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False, CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False, CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True, CONF_USE_POWER_FEATURE: True,
@@ -301,34 +306,43 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100, CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12, CONF_PRESET_POWER: 12,
}, },
) )
entity: ThermostatOverSwitch = await create_thermostat( entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname" hass, entry, "climate.theoverswitchmockname", temps
) )
assert entity assert entity
tpi_algo = entity._prop_algorithm tpi_algo = entity._prop_algorithm
assert tpi_algo assert tpi_algo
tz = get_tz(hass) # pylint: disable=invalid-name now: datetime = NowClass.get_now(hass)
now: datetime = datetime.now(tz=tz) VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_temperature_change_event(entity, 16, now) await send_temperature_change_event(entity, 16, now)
await send_ext_temperature_change_event(entity, 10, now) await send_ext_temperature_change_event(entity, 10, now)
# 1. An already active heater will not switch to overpowering # 1. An already active heater will not switch to overpowering
side_effects = SideEffects(
{
"sensor.the_power_sensor": State("sensor.the_power_sensor", 100),
"sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 110),
},
State("unknown.entity_id", "unknown"),
)
with patch( with patch(
"homeassistant.core.ServiceRegistry.async_call" "homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch( ) as mock_service_call, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active", "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock, new_callable=PropertyMock,
return_value=True, return_value=True,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
): ):
await entity.async_set_hvac_mode(HVACMode.HEAT) await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_preset_mode(PRESET_COMFORT)
@@ -337,14 +351,15 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
assert entity.power_manager.overpowering_state is STATE_UNKNOWN assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 18 assert entity.target_temperature == 18
# waits that the heater starts # waits that the heater starts
await asyncio.sleep(0.1) await hass.async_block_till_done()
assert mock_service_call.call_count >= 1 assert mock_service_call.call_count >= 1
assert entity.is_device_active is True assert entity.is_device_active is True
# Send power max mesurement # Send power max mesurement
await send_max_power_change_event(entity, 110, datetime.now()) await send_max_power_change_event(entity, 110, now)
# Send power mesurement (theheater is already in the power measurement) # Send power mesurement (theheater is already in the power measurement)
await send_power_change_event(entity, 100, datetime.now()) await send_power_change_event(entity, 100, now)
# No overpowering yet # No overpowering yet
assert entity.power_manager.is_overpowering_detected is False assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max # All configuration is complete and power is < power_max
@@ -359,11 +374,22 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active", "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock, new_callable=PropertyMock,
return_value=True, return_value=True,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
): ):
now = now + timedelta(seconds=61)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# change preset to Boost # change preset to Boost
await entity.async_set_preset_mode(PRESET_BOOST) await entity.async_set_preset_mode(PRESET_BOOST)
# waits that the heater starts # waits that the heater starts
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
# doesn't work for call_later
# await hass.async_block_till_done()
# simulate a refresh for central power (not necessary)
await do_central_power_refresh(hass)
assert entity.power_manager.is_overpowering_detected is False assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
@@ -379,12 +405,21 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active", "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock, new_callable=PropertyMock,
return_value=False, return_value=False,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
): ):
now = now + timedelta(seconds=61)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# change preset to Boost # change preset to Boost
await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_preset_mode(PRESET_COMFORT)
# waits that the heater starts # waits that the heater starts
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
# simulate a refresh for central power (not necessary)
await do_central_power_refresh(hass)
assert entity.power_manager.is_overpowering_detected is True assert entity.power_manager.is_overpowering_detected is True
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_POWER assert entity.preset_mode is PRESET_POWER

View File

@@ -188,6 +188,18 @@ async def test_full_over_switch_wo_central_config(
hass: HomeAssistant, skip_hass_states_is_state, init_vtherm_api hass: HomeAssistant, skip_hass_states_is_state, init_vtherm_api
): ):
"""Tests that a VTherm without any central_configuration is working with its own attributes""" """Tests that a VTherm without any central_configuration is working with its own attributes"""
temps = {
"frost": 10,
"eco": 17,
"comfort": 18,
"boost": 21,
"frost_away": 13,
"eco_away": 13,
"comfort_away": 13,
"boost_away": 13,
}
# Add a Switch VTherm # Add a Switch VTherm
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
@@ -202,19 +214,11 @@ async def test_full_over_switch_wo_central_config(
CONF_TEMP_MIN: 8, CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18, CONF_TEMP_MAX: 18,
CONF_STEP_TEMPERATURE: 0.3, CONF_STEP_TEMPERATURE: 0.3,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
"frost_away_temp": 13,
"eco_away_temp": 13,
"comfort_away_temp": 13,
"boost_away_temp": 13,
CONF_USE_WINDOW_FEATURE: True, CONF_USE_WINDOW_FEATURE: True,
CONF_USE_MOTION_FEATURE: True, CONF_USE_MOTION_FEATURE: True,
CONF_USE_POWER_FEATURE: True, CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_HEATER: "switch.mock_switch", CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_INVERSE_SWITCH: False, CONF_INVERSE_SWITCH: False,
CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_INT: 0.3,
@@ -233,8 +237,6 @@ async def test_full_over_switch_wo_central_config(
CONF_MOTION_PRESET: "comfort", CONF_MOTION_PRESET: "comfort",
CONF_NO_MOTION_PRESET: "eco", CONF_NO_MOTION_PRESET: "eco",
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
CONF_USE_MAIN_CENTRAL_CONFIG: False, CONF_USE_MAIN_CENTRAL_CONFIG: False,
CONF_USE_TPI_CENTRAL_CONFIG: False, CONF_USE_TPI_CENTRAL_CONFIG: False,
@@ -249,7 +251,7 @@ async def test_full_over_switch_wo_central_config(
with patch("homeassistant.core.ServiceRegistry.async_call"): with patch("homeassistant.core.ServiceRegistry.async_call"):
entity: ThermostatOverSwitch = await create_thermostat( entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname" hass, entry, "climate.theoverswitchmockname", temps
) )
assert entity assert entity
assert entity.name == "TheOverSwitchMockName" assert entity.name == "TheOverSwitchMockName"
@@ -300,10 +302,13 @@ async def test_full_over_switch_wo_central_config(
assert entity.motion_manager.motion_preset == "comfort" assert entity.motion_manager.motion_preset == "comfort"
assert entity.motion_manager.no_motion_preset == "eco" assert entity.motion_manager.no_motion_preset == "eco"
assert entity.power_manager.power_sensor_entity_id == "sensor.mock_power_sensor"
assert ( assert (
entity.power_manager.max_power_sensor_entity_id VersatileThermostatAPI.get_vtherm_api().central_power_manager.power_sensor_entity_id
== "sensor.mock_max_power_sensor" is None
)
assert (
VersatileThermostatAPI.get_vtherm_api().central_power_manager.max_power_sensor_entity_id
is None
) )
assert ( assert (
@@ -317,7 +322,7 @@ async def test_full_over_switch_wo_central_config(
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_full_over_switch_with_central_config( async def test_full_over_switch_with_central_config(
hass: HomeAssistant, skip_hass_states_is_state, init_central_config hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
): ):
"""Tests that a VTherm with central_configuration is working with the central_config attributes""" """Tests that a VTherm with central_configuration is working with the central_config attributes"""
# Add a Switch VTherm # Add a Switch VTherm
@@ -334,15 +339,11 @@ async def test_full_over_switch_with_central_config(
CONF_TEMP_MIN: 8, CONF_TEMP_MIN: 8,
CONF_TEMP_MAX: 18, CONF_TEMP_MAX: 18,
CONF_STEP_TEMPERATURE: 0.3, CONF_STEP_TEMPERATURE: 0.3,
"frost_temp": 10,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 21,
CONF_USE_WINDOW_FEATURE: True, CONF_USE_WINDOW_FEATURE: True,
CONF_USE_MOTION_FEATURE: True, CONF_USE_MOTION_FEATURE: True,
CONF_USE_POWER_FEATURE: True, CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: True, CONF_USE_PRESENCE_FEATURE: True,
CONF_HEATER: "switch.mock_switch", CONF_UNDERLYING_LIST: ["switch.mock_switch"],
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_INVERSE_SWITCH: False, CONF_INVERSE_SWITCH: False,
CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_INT: 0.3,
@@ -361,8 +362,6 @@ async def test_full_over_switch_with_central_config(
CONF_MOTION_PRESET: "comfort", CONF_MOTION_PRESET: "comfort",
CONF_NO_MOTION_PRESET: "eco", CONF_NO_MOTION_PRESET: "eco",
CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor",
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor",
CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor",
CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_MAIN_CENTRAL_CONFIG: True,
CONF_USE_TPI_CENTRAL_CONFIG: True, CONF_USE_TPI_CENTRAL_CONFIG: True,
@@ -426,10 +425,13 @@ async def test_full_over_switch_with_central_config(
assert entity.motion_manager.motion_preset == "boost" assert entity.motion_manager.motion_preset == "boost"
assert entity.motion_manager.no_motion_preset == "frost" assert entity.motion_manager.no_motion_preset == "frost"
assert entity.power_manager.power_sensor_entity_id == "sensor.mock_power_sensor"
assert ( assert (
entity.power_manager.max_power_sensor_entity_id VersatileThermostatAPI.get_vtherm_api().central_power_manager.power_sensor_entity_id
== "sensor.mock_max_power_sensor" == "sensor.the_power_sensor"
)
assert (
VersatileThermostatAPI.get_vtherm_api().central_power_manager.max_power_sensor_entity_id
== "sensor.the_max_power_sensor"
) )
assert ( assert (

View File

@@ -229,7 +229,7 @@ async def test_central_power_manageer_find_vtherms(
"is_overpowering_detected": False, "is_overpowering_detected": False,
}, },
], ],
{}, {"vtherm1": False},
), ),
# Simple trivial shedding # Simple trivial shedding
( (

View File

@@ -721,10 +721,14 @@ async def test_multiple_climates_underlying_changes_not_aligned(
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True]) @pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_multiple_switch_power_management( async def test_multiple_switch_power_management(
hass: HomeAssistant, skip_hass_states_is_state hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
): ):
"""Test the Power management""" """Test the Power management"""
temps = {
"eco": 17,
"comfort": 18,
"boost": 19,
}
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
title="TheOverSwitchMockName", title="TheOverSwitchMockName",
@@ -737,17 +741,16 @@ async def test_multiple_switch_power_management(
CONF_CYCLE_MIN: 8, CONF_CYCLE_MIN: 8,
CONF_TEMP_MIN: 15, CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30, CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False, CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False, CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True, CONF_USE_POWER_FEATURE: True,
CONF_USE_PRESENCE_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False,
CONF_HEATER: "switch.mock_switch1", CONF_UNDERLYING_LIST: [
CONF_HEATER_2: "switch.mock_switch2", "switch.mock_switch1",
CONF_HEATER_3: "switch.mock_switch3", "switch.mock_switch2",
CONF_HEATER_4: "switch.mock_switch4", "switch.mock_switch3",
"switch.mock_switch4",
],
CONF_HEATER_KEEP_ALIVE: 0, CONF_HEATER_KEEP_ALIVE: 0,
CONF_MINIMAL_ACTIVATION_DELAY: 30, CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5, CONF_SAFETY_DELAY_MIN: 5,
@@ -755,15 +758,13 @@ async def test_multiple_switch_power_management(
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01, CONF_TPI_COEF_EXT: 0.01,
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100, CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12, CONF_PRESET_POWER: 12,
}, },
) )
entity: BaseThermostat = await create_thermostat( entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theover4switchmockname" hass, entry, "climate.theover4switchmockname", temps
) )
assert entity assert entity
assert entity.is_over_climate is False assert entity.is_over_climate is False
@@ -772,6 +773,9 @@ async def test_multiple_switch_power_management(
tpi_algo = entity._prop_algorithm tpi_algo = entity._prop_algorithm
assert tpi_algo assert tpi_algo
now: datetime = NowClass.get_now(hass)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await entity.async_set_hvac_mode(HVACMode.HEAT) await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_BOOST) await entity.async_set_preset_mode(PRESET_BOOST)
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
@@ -780,76 +784,103 @@ async def test_multiple_switch_power_management(
assert entity.target_temperature == 19 assert entity.target_temperature == 19
# 1. Send power mesurement # 1. Send power mesurement
await send_power_change_event(entity, 50, datetime.now()) side_effects = SideEffects(
{
"sensor.the_power_sensor": State("sensor.the_power_sensor", 50),
"sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 300),
},
State("unknown.entity_id", "unknown"),
)
# Send power max mesurement # Send power max mesurement
await send_max_power_change_event(entity, 300, datetime.now()) # fmt:off
assert entity.power_manager.is_overpowering_detected is False with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
# All configuration is complete and power is < power_max # fmt: on
assert entity.preset_mode is PRESET_BOOST now = now + timedelta(seconds=61)
assert entity.power_manager.overpowering_state is STATE_OFF VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_power_change_event(entity, 50, datetime.now())
await send_max_power_change_event(entity, 300, datetime.now())
assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max
assert entity.preset_mode is PRESET_BOOST
assert entity.power_manager.overpowering_state is STATE_OFF
# 2. Send power max mesurement too low and HVACMode is on # 2. Send power max mesurement too low and HVACMode is on
with patch( side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 74))
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
) as mock_heater_on, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
) as mock_heater_off:
# 100 of the device / 4 -> 25, current power 50 so max is 75
await send_max_power_change_event(entity, 74, datetime.now())
assert entity.power_manager.is_overpowering_detected is True
# All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_POWER
assert entity.power_manager.overpowering_state is STATE_ON
assert entity.target_temperature == 12
assert mock_send_event.call_count == 2 with patch(
mock_send_event.assert_has_calls( "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
[ ) as mock_send_event, patch(
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_POWER}), "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
call.send_event( ) as mock_heater_on, patch(
EventType.POWER_EVENT, "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
{ ) as mock_heater_off:
"type": "start",
"current_power": 50, now = now + timedelta(seconds=61)
"device_power": 100, VersatileThermostatAPI.get_vtherm_api()._set_now(now)
"current_max_power": 74, # 100 of the device / 4 -> 25, current power 50 so max is 75
"current_power_consumption": 25.0, await send_max_power_change_event(entity, 74, datetime.now())
}, assert entity.power_manager.is_overpowering_detected is True
), # All configuration is complete and power is > power_max we switch to POWER preset
], assert entity.preset_mode is PRESET_POWER
any_order=True, assert entity.power_manager.overpowering_state is STATE_ON
) assert entity.target_temperature == 12
assert mock_heater_on.call_count == 0
assert mock_heater_off.call_count == 4 # The fourth are shutdown assert mock_send_event.call_count == 2
mock_send_event.assert_has_calls(
[
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_POWER}),
call.send_event(
EventType.POWER_EVENT,
{
"type": "start",
"current_power": 50,
"device_power": 100,
"current_max_power": 74,
"current_power_consumption": 25.0,
},
),
],
any_order=True,
)
assert mock_heater_on.call_count == 0
assert mock_heater_off.call_count == 4 # The fourth are shutdown
# 3. change PRESET # 3. change PRESET
with patch( with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event" "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event: ) as mock_send_event:
await entity.async_set_preset_mode(PRESET_ECO) now = now + timedelta(seconds=61)
assert entity.preset_mode is PRESET_ECO VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# No change
assert entity.power_manager.overpowering_state is STATE_ON await entity.async_set_preset_mode(PRESET_ECO)
assert entity.preset_mode is PRESET_ECO
# No change
assert entity.power_manager.overpowering_state is STATE_ON
# 4. Send hugh power max mesurement to release overpowering # 4. Send hugh power max mesurement to release overpowering
with patch( side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 150))
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
) as mock_heater_on, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
) as mock_heater_off:
# 100 of the device / 4 -> 25, current power 50 so max is 75. With 150 no overheating
await send_max_power_change_event(entity, 150, datetime.now())
assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_ECO
assert entity.power_manager.overpowering_state is STATE_OFF
assert entity.target_temperature == 17
assert ( with patch(
mock_heater_on.call_count == 0 "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) # The fourth are not restarted because temperature is enought ) as mock_send_event, patch(
assert mock_heater_off.call_count == 0 "custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
) as mock_heater_on, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off"
) as mock_heater_off:
now = now + timedelta(seconds=61)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# 100 of the device / 4 -> 25, current power 50 so max is 75. With 150 no overheating
await send_max_power_change_event(entity, 150, datetime.now())
assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_ECO
assert entity.power_manager.overpowering_state is STATE_OFF
assert entity.target_temperature == 17
assert (
mock_heater_on.call_count == 0
) # The fourth are not restarted because temperature is enought
assert mock_heater_off.call_count == 0