FIX merge from #108 have lost some changes
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
# pylint: disable=line-too-long
|
||||||
|
# pylint: disable=too-many-lines
|
||||||
|
# pylint: disable=invalid-name
|
||||||
""" Implements the VersatileThermostat climate component """
|
""" Implements the VersatileThermostat climate component """
|
||||||
import math
|
import math
|
||||||
import logging
|
import logging
|
||||||
@@ -1312,7 +1315,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self.recalculate()
|
self.recalculate()
|
||||||
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
self.send_event(EventType.PRESET_EVENT, {"preset": self._attr_preset_mode})
|
||||||
|
|
||||||
def reset_last_change_time(self, old_preset_mode=None):
|
def reset_last_change_time(
|
||||||
|
self, old_preset_mode=None
|
||||||
|
): # pylint: disable=unused-argument
|
||||||
"""Reset to now the last change time"""
|
"""Reset to now the last change time"""
|
||||||
self._last_change_time = datetime.now(tz=self._current_tz)
|
self._last_change_time = datetime.now(tz=self._current_tz)
|
||||||
_LOGGER.debug("%s - last_change_time is now %s", self, self._last_change_time)
|
_LOGGER.debug("%s - last_change_time is now %s", self, self._last_change_time)
|
||||||
@@ -1546,7 +1551,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# Check delay condition
|
# Check delay condition
|
||||||
async def try_motion_condition(_):
|
async def try_motion_condition(_):
|
||||||
try:
|
try:
|
||||||
delay = self._motion_delay_sec if new_state.state == STATE_ON else self._motion_off_delay_sec
|
delay = (
|
||||||
|
self._motion_delay_sec
|
||||||
|
if new_state.state == STATE_ON
|
||||||
|
else self._motion_off_delay_sec
|
||||||
|
)
|
||||||
long_enough = condition.state(
|
long_enough = condition.state(
|
||||||
self.hass,
|
self.hass,
|
||||||
self._motion_sensor_entity_id,
|
self._motion_sensor_entity_id,
|
||||||
@@ -1583,13 +1592,17 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self._async_control_heating(force=True)
|
await self._async_control_heating(force=True)
|
||||||
self._motion_call_cancel = None
|
self._motion_call_cancel = None
|
||||||
|
|
||||||
im_on = (self._motion_state == STATE_ON)
|
im_on = self._motion_state == STATE_ON
|
||||||
delay_running = (self._motion_call_cancel is not None)
|
delay_running = self._motion_call_cancel is not None
|
||||||
event_on = (new_state.state == STATE_ON)
|
event_on = new_state.state == STATE_ON
|
||||||
|
|
||||||
def arm():
|
def arm():
|
||||||
""" Arm the timer"""
|
"""Arm the timer"""
|
||||||
delay = self._motion_delay_sec if new_state.state == STATE_ON else self._motion_off_delay_sec
|
delay = (
|
||||||
|
self._motion_delay_sec
|
||||||
|
if new_state.state == STATE_ON
|
||||||
|
else self._motion_off_delay_sec
|
||||||
|
)
|
||||||
self._motion_call_cancel = async_call_later(
|
self._motion_call_cancel = async_call_later(
|
||||||
self.hass, timedelta(seconds=delay), try_motion_condition
|
self.hass, timedelta(seconds=delay), try_motion_condition
|
||||||
)
|
)
|
||||||
@@ -1602,7 +1615,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# if I'm off
|
# if I'm off
|
||||||
if not im_on:
|
if not im_on:
|
||||||
if event_on and not delay_running:
|
if event_on and not delay_running:
|
||||||
_LOGGER.debug("%s - Arm delay cause i'm off and event is on and no delay is running", self)
|
_LOGGER.debug(
|
||||||
|
"%s - Arm delay cause i'm off and event is on and no delay is running",
|
||||||
|
self,
|
||||||
|
)
|
||||||
arm()
|
arm()
|
||||||
return try_motion_condition
|
return try_motion_condition
|
||||||
# Ignore the event
|
# Ignore the event
|
||||||
@@ -1614,7 +1630,10 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
arm()
|
arm()
|
||||||
return try_motion_condition
|
return try_motion_condition
|
||||||
if event_on and delay_running:
|
if event_on and delay_running:
|
||||||
_LOGGER.debug("%s - Desarm off delay cause i'm on and event is on and a delay is running", self)
|
_LOGGER.debug(
|
||||||
|
"%s - Desarm off delay cause i'm on and event is on and a delay is running",
|
||||||
|
self,
|
||||||
|
)
|
||||||
desarm()
|
desarm()
|
||||||
return None
|
return None
|
||||||
# Ignore the event
|
# Ignore the event
|
||||||
@@ -1696,9 +1715,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
# Issue 99 - some AC turn hvac_mode=cool and hvac_action=idle when sending a HVACMode_OFF command
|
# Issue 99 - some AC turn hvac_mode=cool and hvac_action=idle when sending a HVACMode_OFF command
|
||||||
# Issue 114 - Remove this because hvac_mode is now managed by local _hvac_mode and use idle action as is
|
# Issue 114 - Remove this because hvac_mode is now managed by local _hvac_mode and use idle action as is
|
||||||
# if self._hvac_mode == HVACMode.OFF and new_hvac_action == HVACAction.IDLE:
|
# if self._hvac_mode == HVACMode.OFF and new_hvac_action == HVACAction.IDLE:
|
||||||
# _LOGGER.debug(
|
# _LOGGER.debug("The underlying switch to idle instead of OFF. We will consider it as OFF")
|
||||||
# "The underlying switch to idle instead of OFF. We will consider it as OFF"
|
|
||||||
# )
|
|
||||||
# new_hvac_mode = HVACMode.OFF
|
# new_hvac_mode = HVACMode.OFF
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
@@ -1710,17 +1727,15 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
old_hvac_action,
|
old_hvac_action,
|
||||||
)
|
)
|
||||||
|
|
||||||
if new_hvac_mode in [
|
_LOGGER.debug(
|
||||||
HVACMode.OFF,
|
"%s - last_change_time=%s old_state_date_changed=%s old_state_date_updated=%s new_state_date_changed=%s new_state_date_updated=%s",
|
||||||
HVACMode.HEAT,
|
self,
|
||||||
HVACMode.COOL,
|
self._last_change_time,
|
||||||
HVACMode.HEAT_COOL,
|
old_state_date_changed,
|
||||||
HVACMode.DRY,
|
old_state_date_updated,
|
||||||
HVACMode.AUTO,
|
new_state_date_changed,
|
||||||
HVACMode.FAN_ONLY,
|
new_state_date_updated,
|
||||||
None,
|
)
|
||||||
]:
|
|
||||||
self._hvac_mode = new_hvac_mode
|
|
||||||
|
|
||||||
# Interpretation of hvac action
|
# Interpretation of hvac action
|
||||||
HVAC_ACTION_ON = [ # pylint: disable=invalid-name
|
HVAC_ACTION_ON = [ # pylint: disable=invalid-name
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
""" Test the Multiple switch management """
|
""" Test the Multiple switch management """
|
||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import patch, call, ANY
|
from unittest.mock import patch, call, ANY
|
||||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||||
|
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ async def test_one_switch_cycle(
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
skip_hass_states_is_state,
|
skip_hass_states_is_state,
|
||||||
skip_send_event,
|
skip_send_event,
|
||||||
):
|
): # pylint: disable=unused-argument
|
||||||
"""Test that when multiple switch are configured the activation is distributed"""
|
"""Test that when multiple switch are configured the activation is distributed"""
|
||||||
|
|
||||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
@@ -75,7 +75,7 @@ async def test_one_switch_cycle(
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.core.StateMachine.is_state", return_value=False
|
"homeassistant.core.StateMachine.is_state", return_value=False
|
||||||
) as mock_is_state:
|
) as mock_is_state:
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
# Should be call for the Switch
|
# Should be call for the Switch
|
||||||
assert mock_is_state.call_count == 1
|
assert mock_is_state.call_count == 1
|
||||||
@@ -132,7 +132,8 @@ async def test_one_switch_cycle(
|
|||||||
assert mock_send_event.call_count == 0
|
assert mock_send_event.call_count == 0
|
||||||
assert mock_heater_off.call_count == 0
|
assert mock_heater_off.call_count == 0
|
||||||
|
|
||||||
# The first heater should be turned on but is already on but because above we mock call_later the heater is not on. But this time it will be really on
|
# The first heater should be turned on but is already on but because above we mock
|
||||||
|
# call_later the heater is not on. But this time it will be really on
|
||||||
assert mock_heater_on.call_count == 1
|
assert mock_heater_on.call_count == 1
|
||||||
|
|
||||||
# Set another temperature at middle level
|
# Set another temperature at middle level
|
||||||
@@ -153,12 +154,15 @@ async def test_one_switch_cycle(
|
|||||||
assert mock_send_event.call_count == 0
|
assert mock_send_event.call_count == 0
|
||||||
assert mock_heater_off.call_count == 0
|
assert mock_heater_off.call_count == 0
|
||||||
|
|
||||||
# The heater is already on cycle. So we wait that the cycle ends and no heater action is done
|
# The heater is already on cycle. So we wait that the cycle ends and no heater action
|
||||||
|
# is done
|
||||||
assert mock_heater_on.call_count == 0
|
assert mock_heater_on.call_count == 0
|
||||||
# assert entity.underlying_entity(0)._should_relaunch_control_heating is True
|
# assert entity.underlying_entity(0)._should_relaunch_control_heating is True
|
||||||
|
|
||||||
# Simulate the relaunch
|
# Simulate the relaunch
|
||||||
await entity.underlying_entity(0)._turn_on_later(None)
|
await entity.underlying_entity(0)._turn_on_later( # pylint: disable=protected-access
|
||||||
|
None
|
||||||
|
)
|
||||||
# wait restart
|
# wait restart
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
@@ -177,7 +181,9 @@ async def test_one_switch_cycle(
|
|||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_device_active:
|
) as mock_device_active:
|
||||||
await entity.underlying_entity(0)._turn_off_later(None)
|
await entity.underlying_entity(0)._turn_off_later( # pylint: disable=protected-access
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
# No special event
|
# No special event
|
||||||
assert mock_send_event.call_count == 0
|
assert mock_send_event.call_count == 0
|
||||||
@@ -198,7 +204,9 @@ async def test_one_switch_cycle(
|
|||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
) as mock_device_active:
|
) as mock_device_active:
|
||||||
await entity.underlying_entity(0)._turn_on_later(None)
|
await entity.underlying_entity(0)._turn_on_later( # pylint: disable=protected-access
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
# No special event
|
# No special event
|
||||||
assert mock_send_event.call_count == 0
|
assert mock_send_event.call_count == 0
|
||||||
@@ -214,7 +222,7 @@ async def test_multiple_switchs(
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
skip_hass_states_is_state,
|
skip_hass_states_is_state,
|
||||||
skip_send_event,
|
skip_send_event,
|
||||||
):
|
): # pylint: disable=unused-argument
|
||||||
"""Test that when multiple switch are configured the activation is distributed"""
|
"""Test that when multiple switch are configured the activation is distributed"""
|
||||||
|
|
||||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
@@ -277,7 +285,7 @@ async def test_multiple_switchs(
|
|||||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||||
|
|
||||||
# Checks that all climates are off
|
# Checks that all climates are off
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
# Should be call for all Switch
|
# Should be call for all Switch
|
||||||
assert mock_underlying_set_hvac_mode.call_count == 4
|
assert mock_underlying_set_hvac_mode.call_count == 4
|
||||||
@@ -342,17 +350,20 @@ async def test_multiple_switchs(
|
|||||||
assert mock_send_event.call_count == 0
|
assert mock_send_event.call_count == 0
|
||||||
assert mock_heater_off.call_count == 0
|
assert mock_heater_off.call_count == 0
|
||||||
|
|
||||||
# The first heater should be turned on but is already on but because call_later is mocked, it is only turned on here
|
# The first heater should be turned on but is already on but because call_later
|
||||||
|
# is mocked, it is only turned on here
|
||||||
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_tasks", [True])
|
||||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||||
async def test_multiple_climates(
|
async def test_multiple_climates(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
skip_hass_states_is_state,
|
skip_hass_states_is_state,
|
||||||
skip_send_event,
|
skip_send_event,
|
||||||
):
|
): # pylint: disable=unused-argument
|
||||||
"""Test that when multiple climates are configured the activation and deactivation is propagated to all climates"""
|
"""Test that when multiple climates are configured the activation and deactivation
|
||||||
|
is propagated to all climates"""
|
||||||
|
|
||||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
now: datetime = datetime.now(tz=tz)
|
now: datetime = datetime.now(tz=tz)
|
||||||
@@ -416,7 +427,7 @@ async def test_multiple_climates(
|
|||||||
call.set_hvac_mode(HVACMode.HEAT),
|
call.set_hvac_mode(HVACMode.HEAT),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
# Stop heating, in boost mode. We block the control_heating to avoid running a cycle
|
# Stop heating, in boost mode. We block the control_heating to avoid running a cycle
|
||||||
with patch(
|
with patch(
|
||||||
@@ -441,7 +452,8 @@ async def test_multiple_climates(
|
|||||||
call.set_hvac_mode(HVACMode.OFF),
|
call.set_hvac_mode(HVACMode.OFF),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||||
@@ -449,8 +461,9 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
skip_hass_states_is_state,
|
skip_hass_states_is_state,
|
||||||
skip_send_event,
|
skip_send_event,
|
||||||
):
|
): # pylint: disable=unused-argument
|
||||||
"""Test that when multiple switch are configured the activation of one underlying climate activate the others"""
|
"""Test that when multiple switch are configured the activation of one underlying
|
||||||
|
climate activate the others"""
|
||||||
|
|
||||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||||
now: datetime = datetime.now(tz=tz)
|
now: datetime = datetime.now(tz=tz)
|
||||||
@@ -514,7 +527,7 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
call.set_hvac_mode(HVACMode.HEAT),
|
call.set_hvac_mode(HVACMode.HEAT),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
# Stop heating on one underlying climate
|
# Stop heating on one underlying climate
|
||||||
with patch(
|
with patch(
|
||||||
@@ -524,7 +537,14 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
) as mock_underlying_set_hvac_mode:
|
) as mock_underlying_set_hvac_mode:
|
||||||
# Wait 11 sec so that the event will not be discarded
|
# Wait 11 sec so that the event will not be discarded
|
||||||
event_timestamp = now + timedelta(seconds=11)
|
event_timestamp = now + timedelta(seconds=11)
|
||||||
await send_climate_change_event(entity, HVACMode.OFF, HVACMode.HEAT, HVACAction.OFF, HVACAction.HEATING, event_timestamp)
|
await send_climate_change_event(
|
||||||
|
entity,
|
||||||
|
HVACMode.OFF,
|
||||||
|
HVACMode.HEAT,
|
||||||
|
HVACAction.OFF,
|
||||||
|
HVACAction.HEATING,
|
||||||
|
event_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
# Should be call for all Switch
|
# Should be call for all Switch
|
||||||
assert mock_underlying_set_hvac_mode.call_count == 4
|
assert mock_underlying_set_hvac_mode.call_count == 4
|
||||||
@@ -534,7 +554,7 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
assert entity.hvac_mode == HVACMode.OFF
|
assert entity.hvac_mode == HVACMode.OFF
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
# Start heating on one underlying climate
|
# Start heating on one underlying climate
|
||||||
with patch(
|
with patch(
|
||||||
@@ -542,12 +562,21 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
), patch(
|
), patch(
|
||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.set_hvac_mode"
|
||||||
) as mock_underlying_set_hvac_mode, patch(
|
) as mock_underlying_set_hvac_mode, patch(
|
||||||
# notice that there is no need of return_value=HVACAction.IDLE because this is not a function but a property
|
# notice that there is no need of return_value=HVACAction.IDLE because this is not
|
||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_action", HVACAction.IDLE
|
# a function but a property
|
||||||
) as mock_underlying_get_hvac_action:
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.hvac_action",
|
||||||
|
HVACAction.IDLE,
|
||||||
|
):
|
||||||
# Wait 11 sec so that the event will not be discarded
|
# Wait 11 sec so that the event will not be discarded
|
||||||
event_timestamp = now + timedelta(seconds=11)
|
event_timestamp = now + timedelta(seconds=11)
|
||||||
await send_climate_change_event(entity, HVACMode.HEAT, HVACMode.OFF, HVACAction.IDLE, HVACAction.OFF, event_timestamp)
|
await send_climate_change_event(
|
||||||
|
entity,
|
||||||
|
HVACMode.HEAT,
|
||||||
|
HVACMode.OFF,
|
||||||
|
HVACAction.IDLE,
|
||||||
|
HVACAction.OFF,
|
||||||
|
event_timestamp,
|
||||||
|
)
|
||||||
|
|
||||||
# Should be call for all Switch
|
# Should be call for all Switch
|
||||||
assert mock_underlying_set_hvac_mode.call_count == 4
|
assert mock_underlying_set_hvac_mode.call_count == 4
|
||||||
@@ -558,5 +587,4 @@ async def test_multiple_climates_underlying_changes(
|
|||||||
)
|
)
|
||||||
assert entity.hvac_mode == HVACMode.HEAT
|
assert entity.hvac_mode == HVACMode.HEAT
|
||||||
assert entity.hvac_action == HVACAction.IDLE
|
assert entity.hvac_action == HVACAction.IDLE
|
||||||
assert entity._is_device_active is False
|
assert entity._is_device_active is False # pylint: disable=protected-access
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user