Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b032198c66 | |||
| 487c118b44 | |||
| e29ff0568b | |||
| 814e4d3b83 | |||
| abb6531f49 |
@@ -3,7 +3,7 @@ default_config:
|
|||||||
logger:
|
logger:
|
||||||
default: info
|
default: info
|
||||||
logs:
|
logs:
|
||||||
custom_components.versatile_thermostat: info
|
custom_components.versatile_thermostat: debug
|
||||||
custom_components.versatile_thermostat.underlyings: debug
|
custom_components.versatile_thermostat.underlyings: debug
|
||||||
custom_components.versatile_thermostat.climate: debug
|
custom_components.versatile_thermostat.climate: debug
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ async def async_setup_entry(
|
|||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_SET_PRESET_TEMPERATURE,
|
SERVICE_SET_PRESET_TEMPERATURE,
|
||||||
{
|
{
|
||||||
vol.Required("preset"): vol.In(CONF_PRESETS),
|
vol.Required("preset"): vol.In(CONF_PRESETS_WITH_AC),
|
||||||
vol.Optional("temperature"): vol.Coerce(float),
|
vol.Optional("temperature"): vol.Coerce(float),
|
||||||
vol.Optional("temperature_away"): vol.Coerce(float),
|
vol.Optional("temperature_away"): vol.Coerce(float),
|
||||||
},
|
},
|
||||||
@@ -966,7 +966,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# else OFF
|
# else OFF
|
||||||
one_idle = False
|
one_idle = False
|
||||||
for under in self._underlyings:
|
for under in self._underlyings:
|
||||||
if action := under.hvac_action not in [HVACAction.IDLE, HVACAction.OFF]:
|
if (action := under.hvac_action) not in [HVACAction.IDLE, HVACAction.OFF]:
|
||||||
return action
|
return action
|
||||||
if under.hvac_action == HVACAction.IDLE:
|
if under.hvac_action == HVACAction.IDLE:
|
||||||
one_idle = True
|
one_idle = True
|
||||||
@@ -1659,6 +1659,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._underlying_climate_delta_t,
|
self._underlying_climate_delta_t,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Manage new target temperature set
|
||||||
|
if self._is_over_climate and new_state.attributes and (new_target_temp := new_state.attributes.get("temperature")) and new_target_temp != self.target_temperature:
|
||||||
|
_LOGGER.info("%s - Target temp have change to %s", self, new_target_temp)
|
||||||
|
await self.async_set_temperature(temperature = new_target_temp)
|
||||||
|
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
await self._async_control_heating()
|
await self._async_control_heating()
|
||||||
|
|
||||||
@@ -2424,8 +2429,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
"""Called by a service call:
|
"""Called by a service call:
|
||||||
service: versatile_thermostat.set_preset_temperature
|
service: versatile_thermostat.set_preset_temperature
|
||||||
data:
|
data:
|
||||||
temperature: 17.8
|
|
||||||
preset: boost
|
preset: boost
|
||||||
|
temperature: 17.8
|
||||||
temperature_away: 15
|
temperature_away: 15
|
||||||
target:
|
target:
|
||||||
entity_id: climate.thermostat_2
|
entity_id: climate.thermostat_2
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
homeassistant==2023.9.0
|
homeassistant==2023.10.1
|
||||||
ffmpeg
|
ffmpeg
|
||||||
@@ -43,6 +43,9 @@ set_preset_temperature:
|
|||||||
- "eco"
|
- "eco"
|
||||||
- "comfort"
|
- "comfort"
|
||||||
- "boost"
|
- "boost"
|
||||||
|
- "eco_ac"
|
||||||
|
- "comfort_ac"
|
||||||
|
- "boost_ac"
|
||||||
temperature:
|
temperature:
|
||||||
name: Temperature when present
|
name: Temperature when present
|
||||||
description: Target temperature for the preset when present
|
description: Target temperature for the preset when present
|
||||||
|
|||||||
@@ -83,6 +83,32 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class MockClimate(ClimateEntity):
|
class MockClimate(ClimateEntity):
|
||||||
"""A Mock Climate class used for Underlying climate mode"""
|
"""A Mock Climate class used for Underlying climate mode"""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos, hvac_mode:HVACMode = HVACMode.OFF) -> None:
|
||||||
|
"""Initialize the thermostat."""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.hass = hass
|
||||||
|
self.platform = 'climate'
|
||||||
|
self.entity_id= self.platform+'.'+unique_id
|
||||||
|
self._attr_extra_state_attributes = {}
|
||||||
|
self._unique_id = unique_id
|
||||||
|
self._name = name
|
||||||
|
self._attr_hvac_action = HVACAction.OFF
|
||||||
|
self._attr_hvac_mode = hvac_mode
|
||||||
|
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
|
||||||
|
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||||
|
self._attr_target_temperature = 20
|
||||||
|
self._attr_current_temperature = 15
|
||||||
|
|
||||||
|
def set_temperature(self, temperature):
|
||||||
|
""" Set the target temperature"""
|
||||||
|
self._attr_target_temperature = temperature
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
class MockUnavailableClimate(ClimateEntity):
|
||||||
|
"""A Mock Climate class used for Underlying climate mode"""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||||
"""Initialize the thermostat."""
|
"""Initialize the thermostat."""
|
||||||
|
|
||||||
@@ -92,12 +118,11 @@ class MockClimate(ClimateEntity):
|
|||||||
self._attr_extra_state_attributes = {}
|
self._attr_extra_state_attributes = {}
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
self._name = name
|
self._name = name
|
||||||
self._attr_hvac_action = HVACAction.OFF
|
self._attr_hvac_action = None
|
||||||
self._attr_hvac_mode = HVACMode.OFF
|
self._attr_hvac_mode = None
|
||||||
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
|
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
|
||||||
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
|
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||||
|
|
||||||
|
|
||||||
class MagicMockClimate(MagicMock):
|
class MagicMockClimate(MagicMock):
|
||||||
"""A Magic Mock class for a underlying climate entity"""
|
"""A Magic Mock class for a underlying climate entity"""
|
||||||
|
|
||||||
@@ -455,6 +480,51 @@ async def send_climate_change_event(
|
|||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
async def send_climate_change_event_with_temperature(
|
||||||
|
entity: VersatileThermostat,
|
||||||
|
new_hvac_mode: HVACMode,
|
||||||
|
old_hvac_mode: HVACMode,
|
||||||
|
new_hvac_action: HVACAction,
|
||||||
|
old_hvac_action: HVACAction,
|
||||||
|
date,
|
||||||
|
temperature,
|
||||||
|
sleep=True,
|
||||||
|
):
|
||||||
|
"""Sending a new climate event simulating a change on the underlying climate state"""
|
||||||
|
_LOGGER.info(
|
||||||
|
"------- Testu: sending send_temperature_change_event, new_hvac_mode=%s old_hvac_mode=%s new_hvac_action=%s old_hvac_action=%s date=%s temperature=%s on %s",
|
||||||
|
new_hvac_mode,
|
||||||
|
old_hvac_mode,
|
||||||
|
new_hvac_action,
|
||||||
|
old_hvac_action,
|
||||||
|
date,
|
||||||
|
temperature,
|
||||||
|
entity,
|
||||||
|
)
|
||||||
|
climate_event = Event(
|
||||||
|
EVENT_STATE_CHANGED,
|
||||||
|
{
|
||||||
|
"new_state": State(
|
||||||
|
entity_id=entity.entity_id,
|
||||||
|
state=new_hvac_mode,
|
||||||
|
attributes={"hvac_action": new_hvac_action, "temperature": temperature},
|
||||||
|
last_changed=date,
|
||||||
|
last_updated=date,
|
||||||
|
),
|
||||||
|
"old_state": State(
|
||||||
|
entity_id=entity.entity_id,
|
||||||
|
state=old_hvac_mode,
|
||||||
|
attributes={"hvac_action": old_hvac_action},
|
||||||
|
last_changed=date,
|
||||||
|
last_updated=date,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ret = await entity._async_climate_changed(climate_event)
|
||||||
|
if sleep:
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def cancel_switchs_cycles(entity: VersatileThermostat):
|
def cancel_switchs_cycles(entity: VersatileThermostat):
|
||||||
"""This method will cancel all running cycle on all underlying switch entity"""
|
"""This method will cancel all running cycle on all underlying switch entity"""
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
""" Test the Window management """
|
""" Test the Window management """
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch, call
|
||||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
@@ -343,3 +343,186 @@ async def test_bug_66(
|
|||||||
assert entity.window_state == STATE_OFF
|
assert entity.window_state == STATE_OFF
|
||||||
assert entity.hvac_mode is HVACMode.HEAT
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
assert entity.preset_mode is PRESET_BOOST
|
assert entity.preset_mode is PRESET_BOOST
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||||
|
async def test_bug_82(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
skip_hass_states_is_state,
|
||||||
|
skip_turn_on_off_heater,
|
||||||
|
skip_send_event,
|
||||||
|
):
|
||||||
|
"""Test that when a underlying climate is not available the VTherm doesn't go into security mode"""
|
||||||
|
|
||||||
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
|
now: datetime = datetime.now(tz=tz)
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="TheOverClimateMockName",
|
||||||
|
unique_id="uniqueId",
|
||||||
|
data=PARTIAL_CLIMATE_CONFIG, # 5 minutes security delay
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_underlying_climate = MockUnavailableClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||||
|
return_value=fake_underlying_climate,
|
||||||
|
) as mock_find_climate:
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
def find_my_entity(entity_id) -> ClimateEntity:
|
||||||
|
"""Find my new entity"""
|
||||||
|
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
|
||||||
|
for entity in component.entities:
|
||||||
|
if entity.entity_id == entity_id:
|
||||||
|
return entity
|
||||||
|
|
||||||
|
entity = find_my_entity("climate.theoverclimatemockname")
|
||||||
|
|
||||||
|
assert entity
|
||||||
|
|
||||||
|
assert entity.name == "TheOverClimateMockName"
|
||||||
|
assert entity._is_over_climate is True
|
||||||
|
# assert entity.hvac_action is HVACAction.OFF
|
||||||
|
# assert entity.hvac_mode is HVACMode.OFF
|
||||||
|
assert entity.hvac_mode is None
|
||||||
|
assert entity.target_temperature == entity.min_temp
|
||||||
|
assert entity.preset_modes == [
|
||||||
|
PRESET_NONE,
|
||||||
|
PRESET_ECO,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_BOOST,
|
||||||
|
]
|
||||||
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
assert entity._security_state is False
|
||||||
|
|
||||||
|
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
|
||||||
|
assert mock_send_event.call_count == 2
|
||||||
|
mock_send_event.assert_has_calls(
|
||||||
|
[
|
||||||
|
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_NONE}),
|
||||||
|
call.send_event(
|
||||||
|
EventType.HVAC_MODE_EVENT,
|
||||||
|
{"hvac_mode": HVACMode.OFF},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_find_climate.call_count == 1
|
||||||
|
assert mock_find_climate.mock_calls[0] == call()
|
||||||
|
mock_find_climate.assert_has_calls([call.find_underlying_entity()])
|
||||||
|
|
||||||
|
# Force security mode
|
||||||
|
assert entity._last_ext_temperature_mesure is not None
|
||||||
|
assert entity._last_temperature_mesure is not None
|
||||||
|
assert (entity._last_temperature_mesure.astimezone(tz) - now).total_seconds() < 1
|
||||||
|
assert (
|
||||||
|
entity._last_ext_temperature_mesure.astimezone(tz) - now
|
||||||
|
).total_seconds() < 1
|
||||||
|
|
||||||
|
# Tries to turns on the Thermostat
|
||||||
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||||
|
assert entity.hvac_mode == None
|
||||||
|
|
||||||
|
# 2. activate security feature when date is expired
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||||
|
) as mock_heater_on:
|
||||||
|
event_timestamp = now - timedelta(minutes=6)
|
||||||
|
|
||||||
|
# set temperature to 15 so that on_percent will be > security_min_on_percent (0.2)
|
||||||
|
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||||
|
# Should stay False
|
||||||
|
assert entity.security_state is False
|
||||||
|
assert entity.preset_mode == 'none'
|
||||||
|
assert entity._saved_preset_mode == 'none'
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||||
|
async def test_bug_101(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
skip_hass_states_is_state,
|
||||||
|
skip_turn_on_off_heater,
|
||||||
|
skip_send_event,
|
||||||
|
):
|
||||||
|
"""Test that when a underlying climate target temp is changed, the VTherm change its own temperature target and switch to manual"""
|
||||||
|
|
||||||
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
|
now: datetime = datetime.now(tz=tz)
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="TheOverClimateMockName",
|
||||||
|
unique_id="uniqueId",
|
||||||
|
data=PARTIAL_CLIMATE_CONFIG, # 5 minutes security delay
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {}, HVACMode.HEAT)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||||
|
return_value=fake_underlying_climate,
|
||||||
|
) as mock_find_climate:
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
def find_my_entity(entity_id) -> ClimateEntity:
|
||||||
|
"""Find my new entity"""
|
||||||
|
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
|
||||||
|
for entity in component.entities:
|
||||||
|
if entity.entity_id == entity_id:
|
||||||
|
return entity
|
||||||
|
|
||||||
|
entity = find_my_entity("climate.theoverclimatemockname")
|
||||||
|
|
||||||
|
assert entity
|
||||||
|
|
||||||
|
assert entity.name == "TheOverClimateMockName"
|
||||||
|
assert entity._is_over_climate is True
|
||||||
|
assert entity.hvac_action is HVACAction.OFF
|
||||||
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
|
assert entity.target_temperature == entity.min_temp
|
||||||
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
|
||||||
|
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
|
||||||
|
assert mock_send_event.call_count == 2
|
||||||
|
mock_send_event.assert_has_calls(
|
||||||
|
[
|
||||||
|
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_NONE}),
|
||||||
|
call.send_event(
|
||||||
|
EventType.HVAC_MODE_EVENT,
|
||||||
|
{"hvac_mode": HVACMode.OFF},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_find_climate.call_count == 1
|
||||||
|
assert mock_find_climate.mock_calls[0] == call()
|
||||||
|
mock_find_climate.assert_has_calls([call.find_underlying_entity()])
|
||||||
|
|
||||||
|
# Force preset mode
|
||||||
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||||
|
assert entity.hvac_mode == HVACMode.HEAT
|
||||||
|
await entity.async_set_preset_mode(PRESET_COMFORT)
|
||||||
|
assert entity.preset_mode == PRESET_COMFORT
|
||||||
|
|
||||||
|
# 2. Change the target temp of underlying thermostat
|
||||||
|
await send_climate_change_event_with_temperature(entity, HVACMode.HEAT, HVACMode.HEAT, HVACAction.OFF, HVACAction.OFF, now, 12.75)
|
||||||
|
# Should have been switched to Manual preset
|
||||||
|
assert entity.target_temperature == 12.75
|
||||||
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -189,3 +189,103 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state):
|
|||||||
|
|
||||||
# Heater is now on
|
# Heater is now on
|
||||||
assert mock_heater_on.call_count == 1
|
assert mock_heater_on.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||||
|
async def test_security_over_climate(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
skip_hass_states_is_state,
|
||||||
|
skip_turn_on_off_heater,
|
||||||
|
skip_send_event,
|
||||||
|
):
|
||||||
|
"""Test that when a underlying climate is not available the VTherm doesn't go into security mode"""
|
||||||
|
|
||||||
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
|
now: datetime = datetime.now(tz=tz)
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="TheOverClimateMockName",
|
||||||
|
unique_id="uniqueId",
|
||||||
|
data=PARTIAL_CLIMATE_CONFIG, # 5 minutes security delay
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {}, HVACMode.HEAT)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||||
|
return_value=fake_underlying_climate,
|
||||||
|
) as mock_find_climate:
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
def find_my_entity(entity_id) -> ClimateEntity:
|
||||||
|
"""Find my new entity"""
|
||||||
|
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
|
||||||
|
for entity in component.entities:
|
||||||
|
if entity.entity_id == entity_id:
|
||||||
|
return entity
|
||||||
|
|
||||||
|
entity = find_my_entity("climate.theoverclimatemockname")
|
||||||
|
|
||||||
|
assert entity
|
||||||
|
|
||||||
|
assert entity.name == "TheOverClimateMockName"
|
||||||
|
assert entity._is_over_climate is True
|
||||||
|
assert entity.hvac_action is HVACAction.OFF
|
||||||
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
|
assert entity.target_temperature == entity.min_temp
|
||||||
|
assert entity.preset_modes == [
|
||||||
|
PRESET_NONE,
|
||||||
|
PRESET_ECO,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_BOOST,
|
||||||
|
]
|
||||||
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
assert entity._security_state is False
|
||||||
|
|
||||||
|
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
|
||||||
|
assert mock_send_event.call_count == 2
|
||||||
|
mock_send_event.assert_has_calls(
|
||||||
|
[
|
||||||
|
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_NONE}),
|
||||||
|
call.send_event(
|
||||||
|
EventType.HVAC_MODE_EVENT,
|
||||||
|
{"hvac_mode": HVACMode.OFF},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_find_climate.call_count == 1
|
||||||
|
assert mock_find_climate.mock_calls[0] == call()
|
||||||
|
mock_find_climate.assert_has_calls([call.find_underlying_entity()])
|
||||||
|
|
||||||
|
# Force security mode
|
||||||
|
assert entity._last_ext_temperature_mesure is not None
|
||||||
|
assert entity._last_temperature_mesure is not None
|
||||||
|
assert (entity._last_temperature_mesure.astimezone(tz) - now).total_seconds() < 1
|
||||||
|
assert (
|
||||||
|
entity._last_ext_temperature_mesure.astimezone(tz) - now
|
||||||
|
).total_seconds() < 1
|
||||||
|
|
||||||
|
# Tries to turns on the Thermostat
|
||||||
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||||
|
assert entity.hvac_mode == HVACMode.HEAT
|
||||||
|
|
||||||
|
# 2. activate security feature when date is expired
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on"
|
||||||
|
) as mock_heater_on:
|
||||||
|
event_timestamp = now - timedelta(minutes=6)
|
||||||
|
|
||||||
|
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||||
|
# Should stay False because a climate is never in security mode
|
||||||
|
assert entity.security_state is False
|
||||||
|
assert entity.preset_mode == 'none'
|
||||||
|
assert entity._saved_preset_mode == 'none'
|
||||||
Reference in New Issue
Block a user