diff --git a/custom_components/versatile_thermostat/base_manager.py b/custom_components/versatile_thermostat/base_manager.py index 63c7a63..c285fe4 100644 --- a/custom_components/versatile_thermostat/base_manager.py +++ b/custom_components/versatile_thermostat/base_manager.py @@ -27,7 +27,7 @@ def post_init(self, entry_infos: ConfigData): """Initialize the attributes of the FeatureManager""" raise NotImplementedError() - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" raise NotImplementedError() diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index 4f6d3b2..fc63336 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -53,7 +53,7 @@ ) from .const import * # pylint: disable=wildcard-import, unused-wildcard-import -from .commons import ConfigData, T, deprecated +from .commons import ConfigData, T from .config_schema import * # pylint: disable=wildcard-import, unused-wildcard-import @@ -478,7 +478,7 @@ async def async_startup(self, central_configuration): # start listening for all managers for manager in self._managers: - manager.start_listening() + await manager.start_listening() await self.get_my_previous_state() @@ -1957,7 +1957,7 @@ def is_preset_configured(self, preset) -> bool: def _set_now(self, now: datetime): """Set the now timestamp. This is only for tests purpose This method should be replaced by the vthermAPI equivalent""" - VersatileThermostatAPI.get_vtherm_api(self._hass)._set_now(now) + VersatileThermostatAPI.get_vtherm_api(self._hass)._set_now(now) # pylint: disable=protected-access # @deprecated @property @@ -1968,8 +1968,8 @@ def now(self) -> datetime: @property def power_percent(self) -> float | None: - """Get the current on_percent as a percentage value. valid only for Vtherm with a TPI algo""" - """Get the current on_percent value""" + """Get the current on_percent as a percentage value. valid only for Vtherm with a TPI algo + Get the current on_percent value""" if self._prop_algorithm and self._prop_algorithm.on_percent is not None: return round(self._prop_algorithm.on_percent * 100, 0) else: diff --git a/custom_components/versatile_thermostat/central_feature_power_manager.py b/custom_components/versatile_thermostat/central_feature_power_manager.py index 1b4f7fd..8762413 100644 --- a/custom_components/versatile_thermostat/central_feature_power_manager.py +++ b/custom_components/versatile_thermostat/central_feature_power_manager.py @@ -4,11 +4,14 @@ from typing import Any from functools import cmp_to_key +from datetime import timedelta + from homeassistant.const import STATE_OFF from homeassistant.core import HomeAssistant, Event, callback from homeassistant.helpers.event import ( async_track_state_change_event, EventStateChangedData, + async_call_later, ) from homeassistant.helpers.entity_component import EntityComponent from homeassistant.components.climate import ( @@ -43,6 +46,8 @@ def __init__(self, hass: HomeAssistant, vtherm_api: Any): self._current_power: float = None self._current_max_power: float = None self._power_temp: float = None + self._cancel_calculate_shedding_call = None + # Not used now self._last_shedding_date = None def post_init(self, entry_infos: ConfigData): @@ -69,7 +74,7 @@ def post_init(self, entry_infos: ConfigData): else: _LOGGER.info("Power management is not fully configured and will be deactivated") - def start_listening(self): + async def start_listening(self): """Start listening the power sensor""" if not self._is_configured: return @@ -110,39 +115,47 @@ async def _max_power_sensor_changed(self, event: Event[EventStateChangedData]): async def refresh_state(self) -> bool: """Tries to get the last state from sensor Returns True if a change has been made""" - ret = False - if self._is_configured: - # try to acquire current power and power max - if ( - new_state := get_safe_float(self._hass, self._power_sensor_entity_id) - ) is not None: - self._current_power = new_state - _LOGGER.debug("Current power have been retrieved: %.3f", self._current_power) - ret = True - - # Try to acquire power max - if ( - new_state := get_safe_float( - self._hass, self._max_power_sensor_entity_id - ) - ) is not None: - self._current_max_power = new_state - _LOGGER.debug("Current power max have been retrieved: %.3f", self._current_max_power) - ret = True - - # check if we need to re-calculate shedding - if ret: - now = self._vtherm_api.now - dtimestamp = ( - (now - self._last_shedding_date).seconds - if self._last_shedding_date - else 999 - ) - if dtimestamp >= MIN_DTEMP_SECS: - await self.calculate_shedding() - self._last_shedding_date = now - - return ret + + async def _calculate_shedding_internal(_): + _LOGGER.debug("Do the shedding calculation") + await self.calculate_shedding() + if self._cancel_calculate_shedding_call: + self._cancel_calculate_shedding_call() + self._cancel_calculate_shedding_call = None + + if not self._is_configured: + return False + + # Retrieve current power + new_power = get_safe_float(self._hass, self._power_sensor_entity_id) + power_changed = new_power is not None and self._current_power != new_power + if power_changed: + self._current_power = new_power + _LOGGER.debug("New current power has been retrieved: %.3f", self._current_power) + + # Retrieve max power + new_max_power = get_safe_float(self._hass, self._max_power_sensor_entity_id) + max_power_changed = new_max_power is not None and self._current_max_power != new_max_power + if max_power_changed: + self._current_max_power = new_max_power + _LOGGER.debug("New current max power has been retrieved: %.3f", self._current_max_power) + + # Schedule shedding calculation if there's any change + if power_changed or max_power_changed: + if not self._cancel_calculate_shedding_call: + self._cancel_calculate_shedding_call = async_call_later(self.hass, timedelta(seconds=MIN_DTEMP_SECS), _calculate_shedding_internal) + return True + + return False + + # For testing purpose only, do an immediate shedding calculation + async def _do_immediate_shedding(self): + """Do an immmediate shedding calculation if a timer was programmed. + Else, do nothing""" + if self._cancel_calculate_shedding_call: + self._cancel_calculate_shedding_call() + self._cancel_calculate_shedding_call = None + await self.calculate_shedding() async def calculate_shedding(self): """Do the shedding calculation and set/unset VTherm into overpowering state""" @@ -197,14 +210,15 @@ async def calculate_shedding(self): ) _LOGGER.debug("vtherm %s power_consumption_max is %s (device_power=%s, overclimate=%s)", vtherm.name, power_consumption_max, device_power, vtherm.is_over_climate) - # if total_power_added + power_consumption_max < available_power or not vtherm.power_manager.is_overpowering_detected: - _LOGGER.info("vtherm %s should not be in overpowering state (power_consumption_max=%.2f)", vtherm.name, power_consumption_max) - # we count the unshedding only if the VTherm was in shedding - if vtherm.power_manager.is_overpowering_detected: - total_power_added += power_consumption_max + # or not ... is for initializing the overpowering state if not already done + if total_power_added + power_consumption_max < available_power or not vtherm.power_manager.is_overpowering_detected: + # we count the unshedding only if the VTherm was in shedding + if vtherm.power_manager.is_overpowering_detected: + _LOGGER.info("vtherm %s should not be in overpowering state (power_consumption_max=%.2f)", vtherm.name, power_consumption_max) + total_power_added += power_consumption_max - await vtherm.power_manager.set_overpowering(False) + await vtherm.power_manager.set_overpowering(False) if total_power_added >= available_power: _LOGGER.debug("We have found enough vtherm to set to non-overpowering") @@ -212,6 +226,7 @@ async def calculate_shedding(self): _LOGGER.debug("after vtherm %s total_power_added=%s, available_power=%s", vtherm.name, total_power_added, available_power) + self._last_shedding_date = self._vtherm_api.now _LOGGER.debug("-------- End of calculate_shedding") def get_climate_components_entities(self) -> list: diff --git a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py index 3880b36..8708563 100644 --- a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py +++ b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py @@ -71,7 +71,7 @@ def post_init(self, entry_infos: ConfigData): ) @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" @overrides diff --git a/custom_components/versatile_thermostat/feature_motion_manager.py b/custom_components/versatile_thermostat/feature_motion_manager.py index 6232386..deaf678 100644 --- a/custom_components/versatile_thermostat/feature_motion_manager.py +++ b/custom_components/versatile_thermostat/feature_motion_manager.py @@ -86,7 +86,7 @@ def post_init(self, entry_infos: ConfigData): self._motion_state = STATE_UNKNOWN @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" if self._is_configured: self.stop_listening() diff --git a/custom_components/versatile_thermostat/feature_power_manager.py b/custom_components/versatile_thermostat/feature_power_manager.py index 1224dee..05fcca5 100644 --- a/custom_components/versatile_thermostat/feature_power_manager.py +++ b/custom_components/versatile_thermostat/feature_power_manager.py @@ -61,19 +61,21 @@ def post_init(self, entry_infos: ConfigData): self._is_configured = False @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity. There is nothing to listen""" central_power_configuration = ( VersatileThermostatAPI.get_vtherm_api().central_power_manager.is_configured ) - if ( - self._use_power_feature - and self._device_power - and central_power_configuration - ): + if self._use_power_feature and self._device_power and central_power_configuration: self._is_configured = True - self._overpowering_state = STATE_UNKNOWN + # Try to restore _overpowering_state from previous state + old_state = await self._vtherm.async_get_last_state() + self._overpowering_state = ( + old_state.attributes.get("overpowering_state", STATE_UNKNOWN) + if old_state and old_state.attributes and old_state.attributes in (STATE_OFF, STATE_ON) + else STATE_UNKNOWN + ) else: if self._use_power_feature: if not central_power_configuration: diff --git a/custom_components/versatile_thermostat/feature_presence_manager.py b/custom_components/versatile_thermostat/feature_presence_manager.py index 5b3ab7d..845e0ee 100644 --- a/custom_components/versatile_thermostat/feature_presence_manager.py +++ b/custom_components/versatile_thermostat/feature_presence_manager.py @@ -67,7 +67,7 @@ def post_init(self, entry_infos: ConfigData): self._presence_state = STATE_UNKNOWN @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" if self._is_configured: self.stop_listening() diff --git a/custom_components/versatile_thermostat/feature_safety_manager.py b/custom_components/versatile_thermostat/feature_safety_manager.py index 3195efe..1c45152 100644 --- a/custom_components/versatile_thermostat/feature_safety_manager.py +++ b/custom_components/versatile_thermostat/feature_safety_manager.py @@ -70,7 +70,7 @@ def post_init(self, entry_infos: ConfigData): self._is_configured = True @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" @overrides diff --git a/custom_components/versatile_thermostat/feature_window_manager.py b/custom_components/versatile_thermostat/feature_window_manager.py index 2497e6a..2c4619f 100644 --- a/custom_components/versatile_thermostat/feature_window_manager.py +++ b/custom_components/versatile_thermostat/feature_window_manager.py @@ -124,7 +124,7 @@ def post_init(self, entry_infos: ConfigData): self._window_state = STATE_UNKNOWN @overrides - def start_listening(self): + async def start_listening(self): """Start listening the underlying entity""" if self._is_configured: self.stop_listening() diff --git a/custom_components/versatile_thermostat/thermostat_switch.py b/custom_components/versatile_thermostat/thermostat_switch.py index 6ae0939..9fef124 100644 --- a/custom_components/versatile_thermostat/thermostat_switch.py +++ b/custom_components/versatile_thermostat/thermostat_switch.py @@ -26,23 +26,21 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): """Representation of a base class for a Versatile Thermostat over a switch.""" - _entity_component_unrecorded_attributes = ( - BaseThermostat._entity_component_unrecorded_attributes.union( - frozenset( - { - "is_over_switch", - "is_inversed", - "underlying_entities", - "on_time_sec", - "off_time_sec", - "cycle_min", - "function", - "tpi_coef_int", - "tpi_coef_ext", - "power_percent", - "calculated_on_percent", - } - ) + _entity_component_unrecorded_attributes = BaseThermostat._entity_component_unrecorded_attributes.union( # pylint: disable=protected-access + frozenset( + { + "is_over_switch", + "is_inversed", + "underlying_entities", + "on_time_sec", + "off_time_sec", + "cycle_min", + "function", + "tpi_coef_int", + "tpi_coef_ext", + "power_percent", + "calculated_on_percent", + } ) ) diff --git a/custom_components/versatile_thermostat/vtherm_api.py b/custom_components/versatile_thermostat/vtherm_api.py index fe35106..7602c72 100644 --- a/custom_components/versatile_thermostat/vtherm_api.py +++ b/custom_components/versatile_thermostat/vtherm_api.py @@ -188,7 +188,7 @@ async def init_vtherm_links(self, entry_id=None): # start listening for the central power manager if not only one vtherm reload if not entry_id: - self.central_power_manager.start_listening() + await self.central_power_manager.start_listening() async def init_vtherm_preset_with_central(self): """Init all VTherm presets when the VTherm uses central temperature""" diff --git a/tests/commons.py b/tests/commons.py index ca78cf7..3a4278a 100644 --- a/tests/commons.py +++ b/tests/commons.py @@ -751,6 +751,7 @@ async def send_power_change_event(entity: BaseThermostat, new_power, date, sleep ) vtherm_api = VersatileThermostatAPI.get_vtherm_api() await vtherm_api.central_power_manager._power_sensor_changed(power_event) + await vtherm_api.central_power_manager._do_immediate_shedding() if sleep: await entity.hass.async_block_till_done() @@ -778,6 +779,7 @@ async def send_max_power_change_event( ) vtherm_api = VersatileThermostatAPI.get_vtherm_api() await vtherm_api.central_power_manager._max_power_sensor_changed(power_event) + await vtherm_api.central_power_manager._do_immediate_shedding() if sleep: await entity.hass.async_block_till_done() diff --git a/tests/test_binary_sensors.py b/tests/test_binary_sensors.py index 7c97c5f..c74f9bc 100644 --- a/tests/test_binary_sensors.py +++ b/tests/test_binary_sensors.py @@ -192,13 +192,13 @@ async def test_overpowering_binary_sensors( assert overpowering_binary_sensor.state == STATE_ON # set max power to a low value - side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 201)) + side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 251)) # fmt:off with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()): # fmt: on now = now + timedelta(seconds=30) VersatileThermostatAPI.get_vtherm_api()._set_now(now) - await send_max_power_change_event(entity, 201, now) + await send_max_power_change_event(entity, 251, now) assert entity.power_manager.is_overpowering_detected is False assert entity.power_manager.overpowering_state is STATE_OFF # Simulate the event reception diff --git a/tests/test_central_power_manager.py b/tests/test_central_power_manager.py index 7880775..f2a4852 100644 --- a/tests/test_central_power_manager.py +++ b/tests/test_central_power_manager.py @@ -59,7 +59,7 @@ async def test_central_power_manager_init( assert central_power_manager.power_temperature == power_temp # 3. start listening - central_power_manager.start_listening() + await central_power_manager.start_listening() assert len(central_power_manager._active_listener) == (2 if is_configured else 0) # 4. stop listening @@ -311,6 +311,54 @@ async def test_central_power_manageer_find_vtherms( # init vtherm1 to False {"vtherm3": False, "vtherm2": False, "vtherm1": False}, ), + # Un-shedding only (will be taken in reverse order) + ( + 1000, + 2000, + [ + # should be not unshedded (too much power will be added) + { + "name": "vtherm1", + "device_power": 800, + "is_device_active": False, + "is_over_climate": False, + "nb_underlying_entities": 1, + "on_percent": 1, + "is_overpowering_detected": True, + "overpowering_state": STATE_ON, + }, + # already stay unshedded cause already unshedded + { + "name": "vtherm2", + "device_power": 100, + "is_device_active": True, + "is_over_climate": True, + "is_overpowering_detected": False, + "overpowering_state": STATE_OFF, + }, + # should be unshedded + { + "name": "vtherm3", + "device_power": 200, + "is_device_active": False, + "is_over_climate": True, + "is_overpowering_detected": True, + "overpowering_state": STATE_ON, + }, + # should be unshedded + { + "name": "vtherm4", + "device_power": 300, + "is_device_active": False, + "is_over_climate": False, + "nb_underlying_entities": 1, + "on_percent": 1, + "is_overpowering_detected": True, + "overpowering_state": STATE_ON, + }, + ], + {"vtherm4": False, "vtherm3": False}, + ), # Shedding ( 2000, @@ -391,65 +439,6 @@ async def test_central_power_manageer_find_vtherms( ], {"vtherm1": True, "vtherm2": True, "vtherm3": True, "vtherm6": True}, ), - # Un-shedding only (will be taken in reverse order) - ( - 1000, - 2000, - [ - # should be not unshedded (we have enough) - { - "name": "vtherm0", - "device_power": 800, - "is_device_active": False, - "is_over_climate": False, - "nb_underlying_entities": 1, - "on_percent": 1, - "is_overpowering_detected": True, - "overpowering_state": STATE_ON, - }, - # should be unshedded - { - "name": "vtherm1", - "device_power": 800, - "is_device_active": False, - "is_over_climate": False, - "nb_underlying_entities": 1, - "on_percent": 1, - "is_overpowering_detected": True, - "overpowering_state": STATE_ON, - }, - # already stay unshedded cause already unshedded - { - "name": "vtherm2", - "device_power": 1100, - "is_device_active": True, - "is_over_climate": True, - "is_overpowering_detected": False, - "overpowering_state": STATE_OFF, - }, - # should be unshedded - { - "name": "vtherm3", - "device_power": 200, - "is_device_active": False, - "is_over_climate": True, - "is_overpowering_detected": True, - "overpowering_state": STATE_ON, - }, - # should be unshedded - { - "name": "vtherm4", - "device_power": 300, - "is_device_active": False, - "is_over_climate": False, - "nb_underlying_entities": 1, - "on_percent": 1, - "is_overpowering_detected": True, - "overpowering_state": STATE_ON, - }, - ], - {"vtherm4": False, "vtherm3": False, "vtherm1": False}, - ), ], ) # @pytest.mark.skip @@ -553,7 +542,7 @@ async def test_central_power_manager_power_event( assert central_power_manager.power_temperature == 13 # 3. start listening (not really useful but don't eat bread) - central_power_manager.start_listening() + await central_power_manager.start_listening() assert len(central_power_manager._active_listener) == 2 now: datetime = NowClass.get_now(hass) @@ -582,6 +571,9 @@ async def test_central_power_manager_power_event( "old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE), })) + if nb_call > 0: + await central_power_manager._do_immediate_shedding() + expected_power = power if isinstance(power, (int, float)) else -999 assert central_power_manager.current_power == expected_power assert mock_calculate_shedding.call_count == nb_call @@ -603,8 +595,11 @@ async def test_central_power_manager_power_event( "old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE), })) + if nb_call > 0: + await central_power_manager._do_immediate_shedding() + assert central_power_manager.current_power == expected_power - assert mock_calculate_shedding.call_count == (nb_call if dsecs >= 20 else 0) + assert mock_calculate_shedding.call_count == nb_call @pytest.mark.parametrize( @@ -645,7 +640,7 @@ async def test_central_power_manager_max_power_event( assert central_power_manager.power_temperature == 13 # 3. start listening (not really useful but don't eat bread) - central_power_manager.start_listening() + await central_power_manager.start_listening() assert len(central_power_manager._active_listener) == 2 now: datetime = NowClass.get_now(hass) @@ -676,6 +671,9 @@ async def test_central_power_manager_max_power_event( "old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE), })) + if nb_call > 0: + await central_power_manager._do_immediate_shedding() + expected_power = max_power if isinstance(max_power, (int, float)) else -999 assert central_power_manager.current_max_power == expected_power assert mock_calculate_shedding.call_count == nb_call @@ -697,5 +695,8 @@ async def test_central_power_manager_max_power_event( "old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE), })) + if nb_call > 0: + await central_power_manager._do_immediate_shedding() + assert central_power_manager.current_max_power == expected_power - assert mock_calculate_shedding.call_count == (nb_call if dsecs >= 20 else 0) + assert mock_calculate_shedding.call_count == nb_call diff --git a/tests/test_motion.py b/tests/test_motion.py index a89a54d..ae7bf00 100644 --- a/tests/test_motion.py +++ b/tests/test_motion.py @@ -90,7 +90,7 @@ async def test_motion_feature_manager_refresh( assert custom_attributes["motion_off_delay_sec"] == 30 # 3. start listening - motion_manager.start_listening() + await motion_manager.start_listening() assert motion_manager.is_configured is True assert motion_manager.motion_state == STATE_UNKNOWN assert motion_manager.is_motion_detected is False @@ -198,7 +198,7 @@ async def test_motion_feature_manager_event( CONF_NO_MOTION_PRESET: PRESET_ECO, } ) - motion_manager.start_listening() + await motion_manager.start_listening() # 2. test _motion_sensor_changed with the parametrized # fmt: off diff --git a/tests/test_power.py b/tests/test_power.py index a311242..72d9f3d 100644 --- a/tests/test_power.py +++ b/tests/test_power.py @@ -98,7 +98,7 @@ async def test_power_feature_manager( } ) - power_manager.start_listening() + await power_manager.start_listening() assert power_manager.is_configured is True assert power_manager.overpowering_state == STATE_UNKNOWN @@ -117,7 +117,7 @@ async def test_power_feature_manager( assert custom_attributes["current_max_power"] is None # 3. start listening - power_manager.start_listening() + await power_manager.start_listening() assert power_manager.is_configured is True assert power_manager.overpowering_state == STATE_UNKNOWN @@ -199,7 +199,7 @@ async def test_power_feature_manager_set_overpowering( } ) - power_manager.start_listening() + await power_manager.start_listening() assert power_manager.is_configured is True assert power_manager.overpowering_state == STATE_UNKNOWN @@ -557,6 +557,7 @@ async def test_power_management_hvac_on( # Send power mesurement low to unset power preset side_effects.add_or_update_side_effect("sensor.the_power_sensor", State("sensor.the_power_sensor", 48)) + side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 149)) # fmt:off with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \ @@ -583,7 +584,7 @@ async def test_power_management_hvac_on( "type": "end", "current_power": 48, "device_power": 100, - "current_max_power": 49, + "current_max_power": 149, }, ), ], diff --git a/tests/test_presence.py b/tests/test_presence.py index c376c0d..d5ed362 100644 --- a/tests/test_presence.py +++ b/tests/test_presence.py @@ -75,7 +75,7 @@ async def test_presence_feature_manager( assert custom_attributes["is_presence_configured"] is True # 3. start listening - presence_manager.start_listening() + await presence_manager.start_listening() assert presence_manager.is_configured is True assert presence_manager.presence_state == STATE_UNKNOWN assert presence_manager.is_absence_detected is False diff --git a/tests/test_window_feature_manager.py b/tests/test_window_feature_manager.py index e6f26ff..62f3110 100644 --- a/tests/test_window_feature_manager.py +++ b/tests/test_window_feature_manager.py @@ -170,7 +170,7 @@ async def test_window_feature_manager_refresh_sensor_action_turn_off( ) # 3. start listening - window_manager.start_listening() + await window_manager.start_listening() assert window_manager.is_configured is True assert window_manager.window_state == STATE_UNKNOWN assert window_manager.window_auto_state == STATE_UNAVAILABLE @@ -288,7 +288,7 @@ async def test_window_feature_manager_refresh_sensor_action_frost_only( ) # 3. start listening - window_manager.start_listening() + await window_manager.start_listening() assert window_manager.is_configured is True assert window_manager.window_state == STATE_UNKNOWN assert window_manager.window_auto_state == STATE_UNAVAILABLE @@ -408,7 +408,7 @@ async def test_window_feature_manager_sensor_event_action_turn_off( ) # 3. start listening - window_manager.start_listening() + await window_manager.start_listening() assert len(window_manager._active_listener) == 1 # 4. test refresh with the parametrized @@ -535,7 +535,7 @@ async def test_window_feature_manager_event_sensor_action_frost_only( ) # 3. start listening - window_manager.start_listening() + await window_manager.start_listening() # 4. test refresh with the parametrized # fmt:off @@ -660,7 +660,7 @@ async def test_window_feature_manager_window_auto( } ) assert window_manager.is_window_auto_configured is True - window_manager.start_listening() + await window_manager.start_listening() # 2. Call manage window auto tz = get_tz(hass) # pylint: disable=invalid-name