|
|
|
|
@@ -1,10 +1,12 @@
|
|
|
|
|
# pylint: disable=unused-argument, line-too-long, protected-access
|
|
|
|
|
""" Test the Window management """
|
|
|
|
|
import asyncio
|
|
|
|
|
import logging
|
|
|
|
|
from unittest.mock import patch, call, PropertyMock
|
|
|
|
|
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
|
|
|
|
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
|
|
|
|
|
|
|
|
|
logging.getLogger().setLevel(logging.DEBUG)
|
|
|
|
|
|
|
|
|
|
@@ -47,7 +49,7 @@ async def test_window_management_time_not_enough(
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -135,7 +137,7 @@ async def test_window_management_time_enough(
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -274,7 +276,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -424,7 +426,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
|
|
|
|
async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_state):
|
|
|
|
|
"""Test the Power management"""
|
|
|
|
|
"""Test the Window auto management"""
|
|
|
|
|
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=DOMAIN,
|
|
|
|
|
@@ -458,7 +460,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -603,7 +605,7 @@ async def test_window_auto_no_on_percent(
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -711,7 +713,7 @@ async def test_window_bypass(
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: VersatileThermostat = await create_thermostat(
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
@@ -746,8 +748,11 @@ async def test_window_bypass(
|
|
|
|
|
assert mock_heater_off.call_count == 0
|
|
|
|
|
assert mock_send_event.call_count == 0
|
|
|
|
|
|
|
|
|
|
#Set Window ByPass to true
|
|
|
|
|
entity._window_bypass_state = True
|
|
|
|
|
# Set Window ByPass to true
|
|
|
|
|
await entity.service_set_window_bypass_state(True)
|
|
|
|
|
assert entity.window_bypass_state is True
|
|
|
|
|
|
|
|
|
|
# entity._window_bypass_state = True
|
|
|
|
|
|
|
|
|
|
# Open the window, condition of time is satisfied, check the thermostat and heater turns off
|
|
|
|
|
with patch(
|
|
|
|
|
@@ -802,5 +807,246 @@ async def test_window_bypass(
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
assert entity.preset_mode is PRESET_BOOST
|
|
|
|
|
|
|
|
|
|
# Clean the entity
|
|
|
|
|
entity.remove_thermostat()
|
|
|
|
|
|
|
|
|
|
#PR - Adding Window bypass for window auto algorithm
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
|
|
|
|
async def test_window_auto_bypass(hass: HomeAssistant, skip_hass_states_is_state):
|
|
|
|
|
"""Test the Window auto management"""
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
"eco_temp": 17,
|
|
|
|
|
"comfort_temp": 18,
|
|
|
|
|
"boost_temp": 21,
|
|
|
|
|
CONF_USE_WINDOW_FEATURE: True,
|
|
|
|
|
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_WINDOW_AUTO_OPEN_THRESHOLD: 0.1,
|
|
|
|
|
CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1,
|
|
|
|
|
CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
|
|
|
|
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
|
|
|
|
now = datetime.now(tz)
|
|
|
|
|
|
|
|
|
|
tpi_algo = entity._prop_algorithm
|
|
|
|
|
assert tpi_algo
|
|
|
|
|
|
|
|
|
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
|
|
|
|
await entity.async_set_preset_mode(PRESET_BOOST)
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
assert entity.preset_mode is PRESET_BOOST
|
|
|
|
|
assert entity.overpowering_state is None
|
|
|
|
|
assert entity.target_temperature == 21
|
|
|
|
|
|
|
|
|
|
assert entity.window_state is None
|
|
|
|
|
|
|
|
|
|
# Make the temperature down
|
|
|
|
|
with patch(
|
|
|
|
|
"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, patch(
|
|
|
|
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
|
|
|
|
return_value=True,
|
|
|
|
|
):
|
|
|
|
|
event_timestamp = now - timedelta(minutes=4)
|
|
|
|
|
await send_temperature_change_event(entity, 19, event_timestamp)
|
|
|
|
|
|
|
|
|
|
# The heater turns on
|
|
|
|
|
assert mock_heater_on.call_count == 1
|
|
|
|
|
assert entity.last_temperature_slope is None
|
|
|
|
|
assert entity._window_auto_algo.is_window_open_detected() is False
|
|
|
|
|
assert entity._window_auto_algo.is_window_close_detected() is False
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
|
|
|
|
|
# send one degre down in one minute with window bypass on
|
|
|
|
|
await entity.service_set_window_bypass_state(True)
|
|
|
|
|
assert entity.window_bypass_state is True
|
|
|
|
|
# entity._window_bypass_state = True
|
|
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
|
"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, patch(
|
|
|
|
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
|
|
|
|
return_value=True,
|
|
|
|
|
):
|
|
|
|
|
event_timestamp = now - timedelta(minutes=3)
|
|
|
|
|
await send_temperature_change_event(entity, 18, event_timestamp, sleep=False)
|
|
|
|
|
|
|
|
|
|
# No change should have been done
|
|
|
|
|
assert mock_send_event.call_count == 0
|
|
|
|
|
|
|
|
|
|
assert mock_heater_on.call_count == 0
|
|
|
|
|
assert mock_heater_off.call_count == 0
|
|
|
|
|
assert entity.last_temperature_slope == -1
|
|
|
|
|
assert entity._window_auto_algo.is_window_open_detected() is True
|
|
|
|
|
assert entity._window_auto_algo.is_window_close_detected() is False
|
|
|
|
|
assert entity.window_auto_state == STATE_OFF
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
|
|
|
|
|
# Clean the entity
|
|
|
|
|
entity.remove_thermostat()
|
|
|
|
|
|
|
|
|
|
#PR - Adding Window bypass AFTER detection have been done should reactivate the heater
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
|
|
|
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
|
|
|
|
async def test_window_bypass_reactivate(hass: HomeAssistant, skip_hass_states_is_state):
|
|
|
|
|
"""Test the Window management when window is open and then bypass is set to on"""
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
"eco_temp": 17,
|
|
|
|
|
"comfort_temp": 18,
|
|
|
|
|
"boost_temp": 19,
|
|
|
|
|
CONF_USE_WINDOW_FEATURE: True,
|
|
|
|
|
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_WINDOW_SENSOR: "binary_sensor.mock_window_sensor",
|
|
|
|
|
CONF_WINDOW_DELAY: 0, # important to not been obliged to wait
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entity: BaseThermostat = await create_thermostat(
|
|
|
|
|
hass, entry, "climate.theoverswitchmockname"
|
|
|
|
|
)
|
|
|
|
|
assert entity
|
|
|
|
|
|
|
|
|
|
tpi_algo = entity._prop_algorithm
|
|
|
|
|
assert tpi_algo
|
|
|
|
|
|
|
|
|
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
|
|
|
|
await entity.async_set_preset_mode(PRESET_BOOST)
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
assert entity.preset_mode is PRESET_BOOST
|
|
|
|
|
assert entity.overpowering_state is None
|
|
|
|
|
assert entity.target_temperature == 19
|
|
|
|
|
|
|
|
|
|
assert entity.window_state is None
|
|
|
|
|
|
|
|
|
|
# change temperature to force turning on the heater
|
|
|
|
|
with patch(
|
|
|
|
|
"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, patch(
|
|
|
|
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
|
|
|
|
return_value=False,
|
|
|
|
|
):
|
|
|
|
|
await send_temperature_change_event(entity, 15, datetime.now())
|
|
|
|
|
|
|
|
|
|
# Heater shoud turn-on
|
|
|
|
|
assert mock_heater_on.call_count >= 1
|
|
|
|
|
assert mock_heater_off.call_count == 0
|
|
|
|
|
assert mock_send_event.call_count == 0
|
|
|
|
|
|
|
|
|
|
# Open the window, condition of time is satisfied, check the thermostat and heater turns off
|
|
|
|
|
with patch(
|
|
|
|
|
"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, patch(
|
|
|
|
|
"homeassistant.helpers.condition.state", return_value=True
|
|
|
|
|
) as mock_condition, patch(
|
|
|
|
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
|
|
|
|
return_value=True,
|
|
|
|
|
):
|
|
|
|
|
await send_window_change_event(entity, True, False, datetime.now())
|
|
|
|
|
|
|
|
|
|
assert mock_send_event.call_count == 1
|
|
|
|
|
mock_send_event.assert_has_calls(
|
|
|
|
|
[call.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.OFF})]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Heater should not be on
|
|
|
|
|
assert mock_heater_on.call_count == 0
|
|
|
|
|
# One call in set_hvac_mode turn_off and one call in the control_heating for security
|
|
|
|
|
assert mock_heater_off.call_count == 2
|
|
|
|
|
assert mock_condition.call_count == 1
|
|
|
|
|
assert entity.hvac_mode is HVACMode.OFF
|
|
|
|
|
assert entity.window_state == STATE_ON
|
|
|
|
|
|
|
|
|
|
# Call the set bypass service to set bypass ON
|
|
|
|
|
with patch(
|
|
|
|
|
"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, patch(
|
|
|
|
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
|
|
|
|
return_value=False,
|
|
|
|
|
):
|
|
|
|
|
await entity.service_set_window_bypass_state(True)
|
|
|
|
|
|
|
|
|
|
assert entity.window_state == STATE_ON
|
|
|
|
|
assert entity.preset_mode is PRESET_BOOST
|
|
|
|
|
assert entity.hvac_mode is HVACMode.HEAT
|
|
|
|
|
# assert mock_heater_on.call_count == 1
|
|
|
|
|
assert mock_send_event.call_count == 1
|
|
|
|
|
mock_send_event.assert_has_calls(
|
|
|
|
|
[
|
|
|
|
|
call.send_event(
|
|
|
|
|
EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.HEAT}
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
any_order=False,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Clean the entity
|
|
|
|
|
entity.remove_thermostat()
|