# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long, unused-variable

""" Test the Auto Start Stop algorithm management """
from datetime import datetime, timedelta
import logging
from unittest.mock import patch, call

from homeassistant.components.climate import HVACMode
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN

from custom_components.versatile_thermostat.thermostat_climate import (
    ThermostatOverClimate,
)
from custom_components.versatile_thermostat.auto_start_stop_algorithm import (
    AutoStartStopDetectionAlgorithm,
    AUTO_START_STOP_ACTION_NOTHING,
    AUTO_START_STOP_ACTION_OFF,
    AUTO_START_STOP_ACTION_ON,
)
from .commons import *  # pylint: disable=wildcard-import, unused-wildcard-import

logging.getLogger().setLevel(logging.DEBUG)


async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
    """Testing directly the algorithm in Slow level"""
    algo: AutoStartStopDetectionAlgorithm = AutoStartStopDetectionAlgorithm(
        AUTO_START_STOP_LEVEL_SLOW, "testu"
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    assert algo._dt == 30
    assert algo._vtherm_name == "testu"

    # 1. should not stop (accumulated_error too low)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=22,
        slope_min=0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.accumulated_error == -1
    assert algo.last_switch_date is None

    # 2. should not stop (accumulated_error too low)
    now = now + timedelta(minutes=5)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=23,
        slope_min=0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.accumulated_error == -6
    assert algo.last_switch_date is None

    # 3. should not stop (accumulated_error too low)
    now = now + timedelta(minutes=2)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=23,
        slope_min=0.1,
        now=now,
    )
    assert algo.accumulated_error == -8
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.last_switch_date is None

    # 4 .No change on accumulated error because the new measure is too near the last one
    now = now + timedelta(seconds=11)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=23,
        slope_min=0.1,
        now=now,
    )
    assert algo.accumulated_error == -8
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.last_switch_date is None

    # 5. should stop now because accumulated_error is > ERROR_THRESHOLD for slow (10)
    now = now + timedelta(minutes=4)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=22,
        slope_min=0.1,
        now=now,
    )
    assert algo.accumulated_error == -10
    assert ret == AUTO_START_STOP_ACTION_OFF
    assert algo.last_switch_date is not None
    assert algo.last_switch_date == now
    last_now = now

    # 6. inverse the temperature (target > current) -> accumulated_error should be divided by 2
    now = now + timedelta(minutes=2)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=22,
        current_temp=21,
        slope_min=-0.1,
        now=now,
    )
    assert algo.accumulated_error == -4  # -10/2 + 1
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.last_switch_date == last_now

    # 7. change level to slow (no real change) -> error_accumulated should not reset to 0
    algo.set_level(AUTO_START_STOP_LEVEL_SLOW)
    assert algo.accumulated_error == -4
    assert algo.last_switch_date == last_now

    # 8. change level -> error_accumulated should reset to 0
    algo.set_level(AUTO_START_STOP_LEVEL_FAST)
    assert algo.accumulated_error == 0
    assert algo.last_switch_date == last_now


async def test_auto_start_stop_too_fast_change(hass: HomeAssistant):
    """Testing directly the algorithm in Slow level"""
    algo: AutoStartStopDetectionAlgorithm = AutoStartStopDetectionAlgorithm(
        AUTO_START_STOP_LEVEL_SLOW, "testu"
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    assert algo._dt == 30
    assert algo._vtherm_name == "testu"

    #
    # Testing with turn_on
    #

    # 1. should stop
    algo._accumulated_error = -100
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=10,
        current_temp=21,
        slope_min=0.5,
        now=now,
    )

    assert ret == AUTO_START_STOP_ACTION_OFF
    assert algo.last_switch_date is not None
    assert algo.last_switch_date == now
    last_now = now

    # 2. now we should turn on but to near the last change -> no nothing to do
    now = now + timedelta(minutes=2)
    algo._accumulated_error = -100
    ret = algo.calculate_action(
        hvac_mode=HVACMode.OFF,
        saved_hvac_mode=HVACMode.HEAT,
        target_temp=21,
        current_temp=17,
        slope_min=-0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.last_switch_date == last_now

    # 3. now we should turn on and now is much later ->
    now = now + timedelta(minutes=30)
    algo._accumulated_error = -100
    ret = algo.calculate_action(
        hvac_mode=HVACMode.OFF,
        saved_hvac_mode=HVACMode.HEAT,
        target_temp=21,
        current_temp=17,
        slope_min=-0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_ON
    assert algo.last_switch_date == now
    last_now = now

    #
    # Testing with turn_off
    #

    # 4. try to turn_off but too speed (29 min)
    now = now + timedelta(minutes=29)
    algo._accumulated_error = -100
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=17,
        current_temp=21,
        slope_min=0.5,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.last_switch_date == last_now

    # 5. turn_off much later (29 min + 1 min)
    now = now + timedelta(minutes=1)
    algo._accumulated_error = -100
    ret = algo.calculate_action(
        hvac_mode=HVACMode.HEAT,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=17,
        current_temp=21,
        slope_min=0.5,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_OFF
    assert algo.last_switch_date == now


async def test_auto_start_stop_algo_medium_cool_off(hass: HomeAssistant):
    """Testing directly the algorithm in Slow level"""
    algo: AutoStartStopDetectionAlgorithm = AutoStartStopDetectionAlgorithm(
        AUTO_START_STOP_LEVEL_MEDIUM, "testu"
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    assert algo._dt == 15
    assert algo._vtherm_name == "testu"

    # 1. should not stop (accumulated_error too low)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.COOL,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=22,
        current_temp=21,
        slope_min=0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.accumulated_error == 1

    # 2. should not stop (accumulated_error too low)
    now = now + timedelta(minutes=3)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.COOL,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=23,
        current_temp=21,
        slope_min=0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_NOTHING
    assert algo.accumulated_error == 4

    # 2. should stop
    now = now + timedelta(minutes=5)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.COOL,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=23,
        current_temp=21,
        slope_min=0.1,
        now=now,
    )
    assert ret == AUTO_START_STOP_ACTION_OFF
    assert algo.accumulated_error == 5  # should be 9 but is capped at error threshold

    # 6. inverse the temperature (target > current) -> accumulated_error should be divided by 2
    now = now + timedelta(minutes=2)
    ret = algo.calculate_action(
        hvac_mode=HVACMode.COOL,
        saved_hvac_mode=HVACMode.OFF,
        target_temp=21,
        current_temp=22,
        slope_min=-0.1,
        now=now,
    )
    assert algo.accumulated_error == 1.5  # 5/2 - 1
    assert ret == AUTO_START_STOP_ACTION_NOTHING


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_none_vtherm(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop is disabled with a real over_climate VTherm in NONE level"""

    # vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: False,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_NONE,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")
        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_NONE
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] is None

    # 1. Vtherm auto-start/stop should be in NONE mode
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_NONE

    # 2. We should not find any switch Enable entity
    assert (
        search_entity(hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN)
        is None
    )


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_medium_heat_vtherm(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop works with a real over_climate VTherm in MEDIUM level"""

    # vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_MEDIUM,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_MEDIUM
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 15

    # 1. Vtherm auto-start/stop should be in MEDIUM mode and an enable entity should exists
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_MEDIUM
    enable_entity = search_entity(
        hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN
    )
    assert enable_entity is not None
    assert enable_entity.state == STATE_ON

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 2. Set mode to Heat and preset to Comfort
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 18, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_COMFORT)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 19.0
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT

    # 3. Set current temperature to 19 5 min later
    now = now + timedelta(minutes=5)
    # reset accumulated error (only for testing)
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 19, now, True)
        await hass.async_block_till_done()

        # VTherm should still be heating
        assert vtherm.hvac_mode == HVACMode.HEAT
        assert mock_send_event.call_count == 0
        assert (
            vtherm._auto_start_stop_algo.accumulated_error == 0
        )  # target = current = 19

    # 4. Set current temperature to 20 5 min later
    now = now + timedelta(minutes=5)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 20, now, True)
        await hass.async_block_till_done()

        # VTherm should still be heating
        assert vtherm.hvac_mode == HVACMode.HEAT
        assert mock_send_event.call_count == 0
        # accumulated_error = target - current = -1 x 5 min / 2
        assert vtherm._auto_start_stop_algo.accumulated_error == -2.5

    # 5. Set current temperature to 21 5 min later -> should turn off
    now = now + timedelta(minutes=5)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 21, now, True)
        await hass.async_block_till_done()

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP

        # accumulated_error = -2.5 + target - current = -2 x 5 min / 2 capped to 5
        assert vtherm._auto_start_stop_algo.accumulated_error == -5

        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "stop",
                        "name": "overClimate",
                        "cause": "Auto stop conditions reached",
                        "hvac_mode": HVACMode.OFF,
                        "saved_hvac_mode": HVACMode.HEAT,
                        "target_temperature": 19.0,
                        "current_temperature": 21.0,
                        "temperature_slope": 0.167,
                        "accumulated_error": -5,
                        "accumulated_error_threshold": 5,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.OFF,
                    },
                )
            ]
        )

    # 6. Set temperature to small over the target, so that it will stay to OFF
    now = now + timedelta(minutes=10)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 19.5, now, True)
        await hass.async_block_till_done()

        # accumulated_error = .... capped to -5
        assert vtherm._auto_start_stop_algo.accumulated_error == -5

        # VTherm should stay stopped cause slope is too low to allow the turn to On
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP

    # 7. Set temperature to over the target, so that it will turn to heat
    now = now + timedelta(minutes=20)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 18, now, True)
        await hass.async_block_till_done()

        # accumulated_error = -5/2 + target - current = 1 x 20 min / 2 capped to 5
        assert vtherm._auto_start_stop_algo.accumulated_error == 5

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.HEAT
        assert vtherm.hvac_off_reason is None

        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "start",
                        "name": "overClimate",
                        "cause": "Auto start conditions reached",
                        "hvac_mode": HVACMode.HEAT,
                        "saved_hvac_mode": HVACMode.HEAT,  # saved don't change
                        "target_temperature": 19.0,
                        "current_temperature": 18.0,
                        "temperature_slope": -0.034,
                        "accumulated_error": 5,
                        "accumulated_error_threshold": 5,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.HEAT,
                    },
                )
            ]
        )


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_fast_ac_vtherm(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop works with a real over_climate VTherm in FAST level and AC mode"""

    # vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_UNDERLYING_LIST: ["climate.mock_climate"],
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in MEDIUM mode
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 2. Set mode to Heat and preset to Comfort
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 27, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.COOL)
    await vtherm.async_set_preset_mode(PRESET_COMFORT)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 25.0
    # VTherm should be cooling
    assert vtherm.hvac_mode == HVACMode.COOL

    # 3. Set current temperature to 19 5 min later
    now = now + timedelta(minutes=5)
    # reset accumulated error for test
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 25, now, True)
        await hass.async_block_till_done()

        # VTherm should still be cooling
        assert vtherm.hvac_mode == HVACMode.COOL
        assert mock_send_event.call_count == 0
        assert (
            vtherm._auto_start_stop_algo.accumulated_error == 0  # target = current = 25
        )

    # 4. Set current temperature to 23 5 min later -> should turn off
    now = now + timedelta(minutes=5)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 23, now, True)
        await hass.async_block_till_done()

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.OFF

        # accumulated_error = target - current = 2 x 5 min / 2 capped to 2
        assert vtherm._auto_start_stop_algo.accumulated_error == 2

        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "stop",
                        "name": "overClimate",
                        "cause": "Auto stop conditions reached",
                        "hvac_mode": HVACMode.OFF,
                        "saved_hvac_mode": HVACMode.COOL,
                        "target_temperature": 25.0,
                        "current_temperature": 23.0,
                        "temperature_slope": -0.28,
                        "accumulated_error": 2,
                        "accumulated_error_threshold": 2,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.OFF,
                    },
                )
            ]
        )

    # 5. Set temperature to over the target, but slope is too low -> no change
    now = now + timedelta(minutes=30)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 25.5, now, True)
        await hass.async_block_till_done()

        # accumulated_error = 2/2 + target - current = -1 x 20 min / 2 capped to 2
        assert vtherm._auto_start_stop_algo.accumulated_error == -2

        # VTherm should stay stopped
        assert vtherm.hvac_mode == HVACMode.OFF
        # a message should have been sent
        assert mock_send_event.call_count == 0

    # 6. Set temperature to over the target, so that it will turn to COOL
    now = now + timedelta(minutes=5)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 26.5, now, True)
        await hass.async_block_till_done()

        # accumulated_error = 2/2 + target - current = -1 x 20 min / 2 capped to 2
        assert vtherm._auto_start_stop_algo.accumulated_error == -2

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.COOL
        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "start",
                        "name": "overClimate",
                        "cause": "Auto start conditions reached",
                        "hvac_mode": HVACMode.COOL,
                        "saved_hvac_mode": HVACMode.COOL,  # saved don't change
                        "target_temperature": 25.0,
                        "current_temperature": 26.5,
                        "temperature_slope": 0.112,
                        "accumulated_error": -2,
                        "accumulated_error_threshold": 2,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.COOL,
                    },
                )
            ]
        )


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_medium_heat_vtherm_preset_change(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop restart a VTherm stopped upon preset_change (in fast mode)"""

    # vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in MEDIUM mode
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 2. Set mode to Heat and preset to Comfort
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 16, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_ECO)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 17.0
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT

    # 3. Set current temperature to 21 5 min later to auto-stop
    now = now + timedelta(minutes=5)
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 19, now, True)
        await hass.async_block_till_done()

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.OFF

        assert vtherm._auto_start_stop_algo.accumulated_error == -2

        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "stop",
                        "name": "overClimate",
                        "cause": "Auto stop conditions reached",
                        "hvac_mode": HVACMode.OFF,
                        "saved_hvac_mode": HVACMode.HEAT,
                        "target_temperature": 17.0,
                        "current_temperature": 19.0,
                        "temperature_slope": 0.3,
                        "accumulated_error": -2,
                        "accumulated_error_threshold": 2,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.OFF,
                    },
                )
            ]
        )

    # 4.1 reduce the slope (because slope is smoothed and was very high)
    now = now + timedelta(minutes=5)
    await send_temperature_change_event(vtherm, 19, now, True)

    now = now + timedelta(minutes=5)
    await send_temperature_change_event(vtherm, 18, now, True)

    now = now + timedelta(minutes=5)
    await send_temperature_change_event(vtherm, 17, now, True)

    # 4. Change preset to auto restart the Vtherm
    now = now + timedelta(minutes=10)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await vtherm.async_set_preset_mode(PRESET_BOOST)
        await hass.async_block_till_done()
        assert vtherm.target_temperature == 21

        assert vtherm._auto_start_stop_algo.accumulated_error == 2

        # VTherm should have been restarted
        assert vtherm.hvac_mode == HVACMode.HEAT
        # a message should have been sent
        assert mock_send_event.call_count >= 1
        mock_send_event.assert_has_calls(
            [
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "start",
                        "name": "overClimate",
                        "cause": "Auto start conditions reached",
                        "hvac_mode": HVACMode.HEAT,
                        "saved_hvac_mode": HVACMode.HEAT,  # saved don't change
                        "target_temperature": 21.0,
                        "current_temperature": 17.0,
                        "temperature_slope": -0.087,
                        "accumulated_error": 2,
                        "accumulated_error_threshold": 2,
                    },
                )
            ]
        )

        mock_send_event.assert_has_calls(
            [
                call(
                    EventType.HVAC_MODE_EVENT,
                    {
                        "hvac_mode": HVACMode.HEAT,
                    },
                )
            ]
        )


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_medium_heat_vtherm_preset_change_enable_false(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop restart a VTherm stopped upon preset_change (in fast mode)"""

    # vtherm_api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in FAST mode and enable should be on
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST
    enable_entity = search_entity(
        hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN
    )
    assert enable_entity is not None
    assert enable_entity.state == STATE_ON

    assert vtherm._attr_extra_state_attributes.get("auto_start_stop_enable") is True

    # 2. set enable to false
    enable_entity.turn_off()
    await hass.async_block_till_done()
    assert enable_entity.state == STATE_OFF
    assert vtherm._attr_extra_state_attributes.get("auto_start_stop_enable") is False

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 3. Set mode to Heat and preset to Comfort
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 16, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_ECO)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 17.0
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT

    # 3. Set current temperature to 21 5 min later to auto-stop
    now = now + timedelta(minutes=5)
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 19, now, True)
        await hass.async_block_till_done()

        # VTherm should not have been stopped cause enable is false
        assert vtherm.hvac_mode == HVACMode.HEAT

        # Not calculated cause enable = false
        assert vtherm._auto_start_stop_algo.accumulated_error == 0

        # a message should have been sent
        assert mock_send_event.call_count == 0


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_fast_heat_window(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop works with a real over_climate VTherm in FAST level and check
    interaction with window openess detection"""

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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: True,
            CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
            CONF_WINDOW_DELAY: 10,
            CONF_USE_MOTION_FEATURE: False,
            CONF_USE_POWER_FEATURE: False,
            CONF_USE_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in MEDIUM mode and an enable entity should exists
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST
    enable_entity = search_entity(
        hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN
    )
    assert enable_entity is not None
    assert enable_entity.state == STATE_ON

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 2. Set mode to Heat and preset to Comfort and close the window
    send_window_change_event(vtherm, False, False, now, False)
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 18, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_COMFORT)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 19.0
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT
    # VTherm window_state should be off
    assert vtherm.window_state == STATE_OFF

    # 3. Set current temperature to 21 5 min later -> should turn off VTherm
    now = now + timedelta(minutes=5)
    # reset accumulated error (only for testing)
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 21, now, True)
        await hass.async_block_till_done()

        # VTherm should no more be heating
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP
        assert vtherm._saved_hvac_mode == HVACMode.HEAT
        assert mock_send_event.call_count == 2

    # 4. Open the window and wait for the delay
    now = now + timedelta(minutes=2)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event, patch(
        "homeassistant.helpers.condition.state", return_value=True
    ) as mock_condition:
        vtherm._set_now(now)
        try_function = await send_window_change_event(
            vtherm, True, False, now, sleep=False
        )

        await try_function(None)

        # Nothing should have change (window event is ignoed as we are already OFF)
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP
        assert vtherm._saved_hvac_mode == HVACMode.HEAT

        mock_send_event.assert_not_called()

        assert vtherm.window_state == STATE_ON

    # 5. close the window
    now = now + timedelta(minutes=2)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event, patch(
        "homeassistant.helpers.condition.state", return_value=True
    ) as mock_condition:
        vtherm._set_now(now)
        try_function = await send_window_change_event(
            vtherm, False, True, now, sleep=False
        )

        await try_function(None)

        # The VTherm should stay off because the reason of off is auto-start-stop
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP
        assert vtherm._saved_hvac_mode == HVACMode.HEAT

        assert mock_send_event.call_count == 0

        assert vtherm.window_state == STATE_OFF


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_fast_heat_window_mixed(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test than auto-start/stop works with a real over_climate VTherm in FAST level and check
    interaction with window openess detection
    The case is when first window on, then auto-stop, then window off and then auto-start
    """

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
        "eco_ac": 27.0,
        "comfort_ac": 25.0,
        "boost_ac": 23.0,
        "frost_away": 7.1,
        "eco_away": 17.1,
        "comfort_away": 19.1,
        "boost_away": 21.1,
        "eco_ac_away": 27.1,
        "comfort_ac_away": 25.1,
        "boost_ac_away": 23.1,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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: True,
            CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
            CONF_WINDOW_DELAY: 10,
            CONF_USE_MOTION_FEATURE: False,
            CONF_USE_POWER_FEATURE: False,
            CONF_USE_AUTO_START_STOP_FEATURE: True,
            CONF_USE_PRESENCE_FEATURE: True,
            CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor",
            CONF_CLIMATE: "climate.mock_climate",
            CONF_MINIMAL_ACTIVATION_DELAY: 30,
            CONF_SECURITY_DELAY_MIN: 5,
            CONF_SECURITY_MIN_ON_PERCENT: 0.3,
            CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: True,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in MEDIUM mode and an enable entity should exists
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST
    enable_entity = search_entity(
        hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN
    )
    assert enable_entity is not None
    assert enable_entity.state == STATE_ON

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    # 2. Set mode to Heat and preset to Comfort and close the window
    send_window_change_event(vtherm, False, False, now, False)
    await send_presence_change_event(vtherm, True, False, now)
    await send_temperature_change_event(vtherm, 18, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_COMFORT)
    await hass.async_block_till_done()

    assert vtherm.target_temperature == 19.0
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT
    # VTherm window_state should be off
    assert vtherm.window_state == STATE_OFF

    # 3. Open the window and wait for the delay
    now = now + timedelta(minutes=2)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event, patch(
        "homeassistant.helpers.condition.state", return_value=True
    ) as mock_condition:
        vtherm._set_now(now)
        try_function = await send_window_change_event(
            vtherm, True, False, now, sleep=False
        )

        await try_function(None)

        # Nothing should have change (window event is ignoed as we are already OFF)
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_WINDOW_DETECTION
        assert vtherm._saved_hvac_mode == HVACMode.HEAT

        assert mock_send_event.call_count == 1

        assert vtherm.window_state == STATE_ON

    # 4. Set current temperature to 21 5 min later -> should turn off VTherm
    now = now + timedelta(minutes=5)
    # reset accumulated error (only for testing)
    vtherm._auto_start_stop_algo._accumulated_error = 0
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 21, now, True)
        await hass.async_block_till_done()

        # VTherm should no more be heating
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_WINDOW_DETECTION  # No change
        assert vtherm._saved_hvac_mode == HVACMode.HEAT
        assert mock_send_event.call_count == 0  # No message

    # 5. close the window
    now = now + timedelta(minutes=2)
    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event, patch(
        "homeassistant.helpers.condition.state", return_value=True
    ) as mock_condition:
        vtherm._set_now(now)
        try_function = await send_window_change_event(
            vtherm, False, True, now, sleep=False
        )

        await try_function(None)

        # The VTherm should turn on and off again due to auto-start-stop
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason is HVAC_OFF_REASON_AUTO_START_STOP
        assert vtherm._saved_hvac_mode == HVACMode.HEAT

        assert vtherm.window_state == STATE_OFF
        assert mock_send_event.call_count >= 2
        mock_send_event.assert_has_calls(
            [
                call(EventType.HVAC_MODE_EVENT, {"hvac_mode": HVACMode.OFF}),
                call(
                    event_type=EventType.AUTO_START_STOP_EVENT,
                    data={
                        "type": "stop",
                        "name": "overClimate",
                        "cause": "Auto stop conditions reached",
                        "hvac_mode": HVACMode.OFF,
                        "saved_hvac_mode": HVACMode.HEAT,
                        "target_temperature": 19.0,
                        "current_temperature": 21.0,
                        "temperature_slope": 0.214,
                        "accumulated_error": -2,
                        "accumulated_error_threshold": 2,
                    },
                ),
            ]
        )


@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_auto_start_stop_disable_vtherm_off(
    hass: HomeAssistant, skip_hass_states_is_state
):
    """Test that if auto-start-stop is disabled while VTherm is off, the VTherms turns on
    This is in the issue #662"""

    # The temperatures to set
    temps = {
        "frost": 7.0,
        "eco": 17.0,
        "comfort": 19.0,
        "boost": 21.0,
    }

    config_entry = MockConfigEntry(
        domain=DOMAIN,
        title="TheOverClimateMockName",
        unique_id="overClimateUniqueId",
        data={
            CONF_NAME: "overClimate",
            CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
            CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
            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_AUTO_START_STOP_FEATURE: True,
            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_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO,
            CONF_AC_MODE: False,
            CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST,
        },
    )

    fake_underlying_climate = MockClimate(
        hass=hass,
        unique_id="mock_climate",
        name="mock_climate",
        hvac_modes=[HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT],
    )

    tz = get_tz(hass)  # pylint: disable=invalid-name
    now: datetime = datetime.now(tz=tz)

    with patch(
        "custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
        return_value=fake_underlying_climate,
    ):
        vtherm: ThermostatOverClimate = await create_thermostat(
            hass, config_entry, "climate.overclimate"
        )

        assert vtherm is not None

        # Initialize all temps
        await set_all_climate_preset_temp(hass, vtherm, temps, "overclimate")

        # Check correct initialization of auto_start_stop attributes
        assert (
            vtherm._attr_extra_state_attributes["auto_start_stop_level"]
            == AUTO_START_STOP_LEVEL_FAST
        )

        assert vtherm._attr_extra_state_attributes["auto_start_stop_dtmin"] == 7

    # 1. Vtherm auto-start/stop should be in FAST mode and enable should be on
    vtherm._set_now(now)
    assert vtherm.auto_start_stop_level == AUTO_START_STOP_LEVEL_FAST
    enable_entity = search_entity(
        hass, "switch.overclimate_enable_auto_start_stop", SWITCH_DOMAIN
    )
    assert enable_entity is not None
    assert enable_entity.state == STATE_ON

    assert vtherm._attr_extra_state_attributes.get("auto_start_stop_enable") is True

    # 2. turn off the VTherm with auto-start/stop
    now = now + timedelta(minutes=5)
    vtherm._set_now(now)
    await send_temperature_change_event(vtherm, 25, now, True)
    await vtherm.async_set_hvac_mode(HVACMode.HEAT)
    await vtherm.async_set_preset_mode(PRESET_ECO)
    await hass.async_block_till_done()

    with patch(
        "custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
    ) as mock_send_event:
        now = now + timedelta(minutes=10)
        vtherm._set_now(now)
        await send_temperature_change_event(vtherm, 26, now, True)
        await hass.async_block_till_done()

        # VTherm should have been stopped
        assert vtherm.hvac_mode == HVACMode.OFF
        assert vtherm.hvac_off_reason == HVAC_OFF_REASON_AUTO_START_STOP

    # 3. set enable to false
    now = now + timedelta(minutes=5)
    vtherm._set_now(now)

    enable_entity.turn_off()
    await hass.async_block_till_done()
    assert enable_entity.state == STATE_OFF
    # VTherm should be heating
    assert vtherm.hvac_mode == HVACMode.HEAT
    # In Eco
    assert vtherm.target_temperature == 17.0
