Compare commits

..

4 Commits

Author SHA1 Message Date
Jean-Marc Collin
8b58e69755 Fix restore overpowering_state 2025-01-05 10:53:28 +00:00
Jean-Marc Collin
81231f977c Add temporal filter for calculate_shedding
Add restore overpowering state at startup
2025-01-05 10:30:34 +00:00
Jean-Marc Collin
6bdcecefac Add logs 2025-01-05 08:47:00 +00:00
Jean-Marc Collin
34181b4204 Fix too much shedding in over_climate 2025-01-05 08:44:59 +00:00
21 changed files with 269 additions and 281 deletions

View File

@@ -27,7 +27,7 @@ class BaseFeatureManager:
"""Initialize the attributes of the FeatureManager""" """Initialize the attributes of the FeatureManager"""
raise NotImplementedError() raise NotImplementedError()
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
raise NotImplementedError() raise NotImplementedError()

View File

@@ -53,7 +53,7 @@ from homeassistant.const import (
) )
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import 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 from .config_schema import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -98,7 +98,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
"comfort_away_temp", "comfort_away_temp",
"power_temp", "power_temp",
"ac_mode", "ac_mode",
"current_max_power",
"saved_preset_mode", "saved_preset_mode",
"saved_target_temp", "saved_target_temp",
"saved_hvac_mode", "saved_hvac_mode",
@@ -478,7 +477,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
# start listening for all managers # start listening for all managers
for manager in self._managers: for manager in self._managers:
manager.start_listening() await manager.start_listening()
await self.get_my_previous_state() await self.get_my_previous_state()
@@ -1609,14 +1608,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
return False return False
# Check overpowering condition # Check overpowering condition
await VersatileThermostatAPI.get_vtherm_api().central_power_manager.refresh_state() # Not usefull. Will be done at the next power refresh
# await VersatileThermostatAPI.get_vtherm_api().central_power_manager.refresh_state()
# TODO remove this
# overpowering is now centralized
# overpowering = await self._power_manager.check_overpowering()
# if overpowering == STATE_ON:
# _LOGGER.debug("%s - End of cycle (overpowering)", self)
# return True
safety: bool = await self._safety_manager.refresh_state() safety: bool = await self._safety_manager.refresh_state()
if safety and self.is_over_climate: if safety and self.is_over_climate:
@@ -1963,7 +1956,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
def _set_now(self, now: datetime): def _set_now(self, now: datetime):
"""Set the now timestamp. This is only for tests purpose """Set the now timestamp. This is only for tests purpose
This method should be replaced by the vthermAPI equivalent""" 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 # @deprecated
@property @property
@@ -1974,5 +1967,17 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
@property @property
def power_percent(self) -> float | None: 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"""
if self._prop_algorithm and self._prop_algorithm.on_percent is not None:
return round(self._prop_algorithm.on_percent * 100, 0)
else:
return None
@property
def on_percent(self) -> float | None:
"""Get the current on_percent value. valid only for Vtherm with a TPI algo""" """Get the current on_percent value. valid only for Vtherm with a TPI algo"""
return None if self._prop_algorithm and self._prop_algorithm.on_percent is not None:
return self._prop_algorithm.on_percent
else:
return None

View File

@@ -4,10 +4,14 @@ import logging
from typing import Any from typing import Any
from functools import cmp_to_key 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.core import HomeAssistant, Event, callback
from homeassistant.helpers.event import ( from homeassistant.helpers.event import (
async_track_state_change_event, async_track_state_change_event,
EventStateChangedData, EventStateChangedData,
async_call_later,
) )
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.climate import ( from homeassistant.components.climate import (
@@ -42,6 +46,8 @@ class CentralFeaturePowerManager(BaseFeatureManager):
self._current_power: float = None self._current_power: float = None
self._current_max_power: float = None self._current_max_power: float = None
self._power_temp: float = None self._power_temp: float = None
self._cancel_calculate_shedding_call = None
# Not used now
self._last_shedding_date = None self._last_shedding_date = None
def post_init(self, entry_infos: ConfigData): def post_init(self, entry_infos: ConfigData):
@@ -68,7 +74,7 @@ class CentralFeaturePowerManager(BaseFeatureManager):
else: else:
_LOGGER.info("Power management is not fully configured and will be deactivated") _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""" """Start listening the power sensor"""
if not self._is_configured: if not self._is_configured:
return return
@@ -109,45 +115,54 @@ class CentralFeaturePowerManager(BaseFeatureManager):
async def refresh_state(self) -> bool: async def refresh_state(self) -> bool:
"""Tries to get the last state from sensor """Tries to get the last state from sensor
Returns True if a change has been made""" 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 async def _calculate_shedding_internal(_):
if ( _LOGGER.debug("Do the shedding calculation")
new_state := get_safe_float( await self.calculate_shedding()
self._hass, self._max_power_sensor_entity_id if self._cancel_calculate_shedding_call:
) self._cancel_calculate_shedding_call()
) is not None: self._cancel_calculate_shedding_call = 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 not self._is_configured:
if ret: return False
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 # 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): async def calculate_shedding(self):
"""Do the shedding calculation and set/unset VTherm into overpowering state""" """Do the shedding calculation and set/unset VTherm into overpowering state"""
if not self.is_configured or self.current_max_power is None or self.current_power is None: if not self.is_configured or self.current_max_power is None or self.current_power is None:
return return
_LOGGER.debug("-------- Start of calculate_shedding")
# Find all VTherms # Find all VTherms
available_power = self.current_max_power - self.current_power available_power = self.current_max_power - self.current_power
vtherms_sorted = self.find_all_vtherm_with_power_management_sorted_by_dtemp() vtherms_sorted = self.find_all_vtherm_with_power_management_sorted_by_dtemp()
@@ -163,54 +178,56 @@ class CentralFeaturePowerManager(BaseFeatureManager):
total_power_gain = 0 total_power_gain = 0
for vtherm in vtherms_sorted: for vtherm in vtherms_sorted:
device_power = vtherm.power_manager.device_power
if vtherm.is_device_active and not vtherm.power_manager.is_overpowering_detected: if vtherm.is_device_active and not vtherm.power_manager.is_overpowering_detected:
device_power = vtherm.power_manager.device_power
total_power_gain += device_power total_power_gain += device_power
_LOGGER.debug("vtherm %s should be in overpowering state", vtherm.name) _LOGGER.info("vtherm %s should be in overpowering state (device_power=%.2f)", vtherm.name, device_power)
await vtherm.power_manager.set_overpowering(True, device_power) await vtherm.power_manager.set_overpowering(True, device_power)
_LOGGER.debug("after vtherm %s total_power_gain=%s, available_power=%s", vtherm.name, total_power_gain, available_power) _LOGGER.debug("after vtherm %s total_power_gain=%s, available_power=%s", vtherm.name, total_power_gain, available_power)
if total_power_gain >= -available_power: if total_power_gain >= -available_power:
_LOGGER.debug("We have found enough vtherm to set to overpowering") _LOGGER.debug("We have found enough vtherm to set to overpowering")
break break
# unshedding only
else: else:
# vtherms_sorted.reverse() vtherms_sorted.reverse()
_LOGGER.debug("The available power is is > 0 (%s). Do a complete shedding/un-shedding calculation for list: %s", available_power, vtherms_sorted) _LOGGER.debug("The available power is is > 0 (%s). Do a complete shedding/un-shedding calculation for list: %s", available_power, vtherms_sorted)
total_affected_power = 0 total_power_added = 0
force_overpowering = False
for vtherm in vtherms_sorted: for vtherm in vtherms_sorted:
device_power = vtherm.power_manager.device_power # We want to do always unshedding in order to initialize the state
# so we cannot use is_overpowering_detected which test also UNKNOWN and UNAVAILABLE
if vtherm.power_manager.overpowering_state == STATE_OFF:
continue
power_consumption_max = device_power = vtherm.power_manager.device_power
# calculate the power_consumption_max # calculate the power_consumption_max
if vtherm.is_device_active: if vtherm.on_percent is not None:
power_consumption_max = 0 power_consumption_max = max(
else: device_power / vtherm.nb_underlying_entities,
if vtherm.is_over_climate: device_power * vtherm.on_percent,
power_consumption_max = device_power )
else:
if vtherm.proportional_algorithm.on_percent > 0:
power_consumption_max = max(
device_power / vtherm.nb_underlying_entities,
device_power * vtherm.proportional_algorithm.on_percent,
)
else:
power_consumption_max = 0
_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) _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 force_overpowering or (total_affected_power + power_consumption_max >= available_power):
_LOGGER.debug("vtherm %s should be in overpowering state", vtherm.name) # or not ... is for initializing the overpowering state if not already done
if not vtherm.power_manager.is_overpowering_detected: if total_power_added + power_consumption_max < available_power or not vtherm.power_manager.is_overpowering_detected:
# To force all others vtherms to be in overpowering # we count the unshedding only if the VTherm was in shedding
force_overpowering = True if vtherm.power_manager.is_overpowering_detected:
await vtherm.power_manager.set_overpowering(True, power_consumption_max) _LOGGER.info("vtherm %s should not be in overpowering state (power_consumption_max=%.2f)", vtherm.name, power_consumption_max)
else: total_power_added += power_consumption_max
total_affected_power += power_consumption_max
# Always set to false to init the state
_LOGGER.debug("vtherm %s should not be in overpowering state", vtherm.name)
await vtherm.power_manager.set_overpowering(False) await vtherm.power_manager.set_overpowering(False)
_LOGGER.debug("after vtherm %s total_affected_power=%s, available_power=%s", vtherm.name, total_affected_power, available_power) if total_power_added >= available_power:
_LOGGER.debug("We have found enough vtherm to set to non-overpowering")
break
_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: def get_climate_components_entities(self) -> list:
"""Get all VTherms entitites""" """Get all VTherms entitites"""

View File

@@ -71,7 +71,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
) )
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
@overrides @overrides

View File

@@ -86,7 +86,7 @@ class FeatureMotionManager(BaseFeatureManager):
self._motion_state = STATE_UNKNOWN self._motion_state = STATE_UNKNOWN
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
if self._is_configured: if self._is_configured:
self.stop_listening() self.stop_listening()

View File

@@ -61,19 +61,17 @@ class FeaturePowerManager(BaseFeatureManager):
self._is_configured = False self._is_configured = False
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity. There is nothing to listen""" """Start listening the underlying entity. There is nothing to listen"""
central_power_configuration = ( central_power_configuration = (
VersatileThermostatAPI.get_vtherm_api().central_power_manager.is_configured VersatileThermostatAPI.get_vtherm_api().central_power_manager.is_configured
) )
if ( if self._use_power_feature and self._device_power and central_power_configuration:
self._use_power_feature
and self._device_power
and central_power_configuration
):
self._is_configured = True 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 = STATE_ON if old_state and old_state.attributes and old_state.attributes.get("overpowering_state") == STATE_ON else STATE_UNKNOWN
else: else:
if self._use_power_feature: if self._use_power_feature:
if not central_power_configuration: if not central_power_configuration:

View File

@@ -67,7 +67,7 @@ class FeaturePresenceManager(BaseFeatureManager):
self._presence_state = STATE_UNKNOWN self._presence_state = STATE_UNKNOWN
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
if self._is_configured: if self._is_configured:
self.stop_listening() self.stop_listening()

View File

@@ -70,7 +70,7 @@ class FeatureSafetyManager(BaseFeatureManager):
self._is_configured = True self._is_configured = True
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
@overrides @overrides

View File

@@ -124,7 +124,7 @@ class FeatureWindowManager(BaseFeatureManager):
self._window_state = STATE_UNKNOWN self._window_state = STATE_UNKNOWN
@overrides @overrides
def start_listening(self): async def start_listening(self):
"""Start listening the underlying entity""" """Start listening the underlying entity"""
if self._is_configured: if self._is_configured:
self.stop_listening() self.stop_listening()

View File

@@ -263,15 +263,6 @@ class ThermostatOverClimateValve(ThermostatOverClimate):
"""True if the Thermostat is regulated by valve""" """True if the Thermostat is regulated by valve"""
return True return True
@overrides
@property
def power_percent(self) -> float | None:
"""Get the current on_percent value"""
if self._prop_algorithm:
return round(self._prop_algorithm.on_percent * 100, 0)
else:
return None
# @property # @property
# def hvac_modes(self) -> list[HVACMode]: # def hvac_modes(self) -> list[HVACMode]:
# """Get the hvac_modes""" # """Get the hvac_modes"""

View File

@@ -26,23 +26,21 @@ _LOGGER = logging.getLogger(__name__)
class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
"""Representation of a base class for a Versatile Thermostat over a switch.""" """Representation of a base class for a Versatile Thermostat over a switch."""
_entity_component_unrecorded_attributes = ( _entity_component_unrecorded_attributes = BaseThermostat._entity_component_unrecorded_attributes.union( # pylint: disable=protected-access
BaseThermostat._entity_component_unrecorded_attributes.union( frozenset(
frozenset( {
{ "is_over_switch",
"is_over_switch", "is_inversed",
"is_inversed", "underlying_entities",
"underlying_entities", "on_time_sec",
"on_time_sec", "off_time_sec",
"off_time_sec", "cycle_min",
"cycle_min", "function",
"function", "tpi_coef_int",
"tpi_coef_int", "tpi_coef_ext",
"tpi_coef_ext", "power_percent",
"power_percent", "calculated_on_percent",
"calculated_on_percent", }
}
)
) )
) )
@@ -61,15 +59,6 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
"""True if the switch is inversed (for pilot wire and diode)""" """True if the switch is inversed (for pilot wire and diode)"""
return self._is_inversed is True return self._is_inversed is True
@overrides
@property
def power_percent(self) -> float | None:
"""Get the current on_percent value"""
if self._prop_algorithm:
return round(self._prop_algorithm.on_percent * 100, 0)
else:
return None
@overrides @overrides
def post_init(self, config_entry: ConfigData): def post_init(self, config_entry: ConfigData):
"""Initialize the Thermostat""" """Initialize the Thermostat"""

View File

@@ -188,7 +188,7 @@ class VersatileThermostatAPI(dict):
# start listening for the central power manager if not only one vtherm reload # start listening for the central power manager if not only one vtherm reload
if not entry_id: 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): async def init_vtherm_preset_with_central(self):
"""Init all VTherm presets when the VTherm uses central temperature""" """Init all VTherm presets when the VTherm uses central temperature"""

View File

@@ -751,6 +751,7 @@ async def send_power_change_event(entity: BaseThermostat, new_power, date, sleep
) )
vtherm_api = VersatileThermostatAPI.get_vtherm_api() vtherm_api = VersatileThermostatAPI.get_vtherm_api()
await vtherm_api.central_power_manager._power_sensor_changed(power_event) await vtherm_api.central_power_manager._power_sensor_changed(power_event)
await vtherm_api.central_power_manager._do_immediate_shedding()
if sleep: if sleep:
await entity.hass.async_block_till_done() await entity.hass.async_block_till_done()
@@ -778,6 +779,7 @@ async def send_max_power_change_event(
) )
vtherm_api = VersatileThermostatAPI.get_vtherm_api() vtherm_api = VersatileThermostatAPI.get_vtherm_api()
await vtherm_api.central_power_manager._max_power_sensor_changed(power_event) await vtherm_api.central_power_manager._max_power_sensor_changed(power_event)
await vtherm_api.central_power_manager._do_immediate_shedding()
if sleep: if sleep:
await entity.hass.async_block_till_done() await entity.hass.async_block_till_done()

View File

@@ -172,16 +172,17 @@ async def test_overpowering_binary_sensors(
# Send power mesurement # Send power mesurement
side_effects = SideEffects( side_effects = SideEffects(
{ {
"sensor.the_power_sensor": State("sensor.the_power_sensor", 100), "sensor.the_power_sensor": State("sensor.the_power_sensor", 150),
"sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 150), "sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 100),
}, },
State("unknown.entity_id", "unknown"), State("unknown.entity_id", "unknown"),
) )
# fmt:off # fmt:off
with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()): with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \
patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
# fmt: on # fmt: on
await send_power_change_event(entity, 100, now) await send_power_change_event(entity, 150, now)
await send_max_power_change_event(entity, 150, now) await send_max_power_change_event(entity, 100, now)
assert entity.power_manager.is_overpowering_detected is True assert entity.power_manager.is_overpowering_detected is True
assert entity.power_manager.overpowering_state is STATE_ON assert entity.power_manager.overpowering_state is STATE_ON
@@ -191,13 +192,13 @@ async def test_overpowering_binary_sensors(
assert overpowering_binary_sensor.state == STATE_ON assert overpowering_binary_sensor.state == STATE_ON
# set max power to a low value # 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 # fmt:off
with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()): with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()):
# fmt: on # fmt: on
now = now + timedelta(seconds=30) now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now) 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.is_overpowering_detected is False
assert entity.power_manager.overpowering_state is STATE_OFF assert entity.power_manager.overpowering_state is STATE_OFF
# Simulate the event reception # Simulate the event reception

View File

@@ -348,7 +348,7 @@ async def test_bug_407(
await entity.async_set_preset_mode(PRESET_COMFORT) await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_COMFORT assert entity.preset_mode is PRESET_COMFORT
assert entity.power_manager.overpowering_state is STATE_OFF assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 18 assert entity.target_temperature == 18
# waits that the heater starts # waits that the heater starts
await hass.async_block_till_done() await hass.async_block_till_done()
@@ -398,7 +398,8 @@ async def test_bug_407(
assert entity.target_temperature == 19 assert entity.target_temperature == 19
assert mock_service_call.call_count >= 1 assert mock_service_call.call_count >= 1
# 3. if heater is stopped (is_device_active==False), then overpowering should be started # 3. if heater is stopped (is_device_active==False) and power is over max, then overpowering should be started
side_effects.add_or_update_side_effect("sensor.the_power_sensor", State("sensor.the_power_sensor", 150))
with patch( with patch(
"homeassistant.core.ServiceRegistry.async_call" "homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch( ) as mock_service_call, patch(
@@ -420,10 +421,10 @@ async def test_bug_407(
# simulate a refresh for central power (not necessary) # simulate a refresh for central power (not necessary)
await do_central_power_refresh(hass) await do_central_power_refresh(hass)
assert entity.power_manager.is_overpowering_detected is True assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_POWER assert entity.preset_mode is PRESET_COMFORT
assert entity.power_manager.overpowering_state is STATE_ON assert entity.power_manager.overpowering_state is STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True]) @pytest.mark.parametrize("expected_lingering_tasks", [True])

View File

@@ -59,7 +59,7 @@ async def test_central_power_manager_init(
assert central_power_manager.power_temperature == power_temp assert central_power_manager.power_temperature == power_temp
# 3. start listening # 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) assert len(central_power_manager._active_listener) == (2 if is_configured else 0)
# 4. stop listening # 4. stop listening
@@ -273,7 +273,7 @@ async def test_central_power_manageer_find_vtherms(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"current_power, current_max_power, vtherm_configs, expected_results", "current_power, current_max_power, vtherm_configs, expected_results",
[ [
# simple nominal test (no shedding) # simple nominal test (initialize overpowering state in VTherm)
( (
1000, 1000,
5000, 5000,
@@ -286,139 +286,80 @@ async def test_central_power_manageer_find_vtherms(
"nb_underlying_entities": 1, "nb_underlying_entities": 1,
"on_percent": 0, "on_percent": 0,
"is_overpowering_detected": False, "is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
}, },
],
{"vtherm1": False},
),
# Simple trivial shedding
(
1000,
2000,
[
# should be overpowering
{
"name": "vtherm1",
"device_power": 1100,
"is_device_active": False,
"is_over_climate": False,
"nb_underlying_entities": 1,
"on_percent": 1,
"is_overpowering_detected": False,
},
# should be overpowering with many underlmying entities
{ {
"name": "vtherm2", "name": "vtherm2",
"device_power": 4000, "device_power": 10000,
"is_device_active": False, "is_device_active": True,
"is_over_climate": False, "is_over_climate": False,
"nb_underlying_entities": 4, "nb_underlying_entities": 4,
"on_percent": 0.1, "on_percent": 100,
"is_overpowering_detected": False, "is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
}, },
# over_climate should be overpowering
{ {
"name": "vtherm3", "name": "vtherm3",
"device_power": 1000, "device_power": 5000,
"is_device_active": False, "is_device_active": True,
"is_over_climate": True, "is_over_climate": True,
"is_overpowering_detected": False, "is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
}, },
# should pass but because will be also overpowering because previous was overpowering {"name": "vtherm4", "device_power": 1000, "is_device_active": True, "is_over_climate": True, "is_overpowering_detected": False, "overpowering_state": STATE_OFF},
{
"name": "vtherm4",
"device_power": 800,
"is_device_active": False,
"is_over_climate": False,
"nb_underlying_entities": 1,
"on_percent": 1,
"is_overpowering_detected": False,
},
], ],
{"vtherm1": True, "vtherm2": True, "vtherm3": True, "vtherm4": True}, # init vtherm1 to False
{"vtherm3": False, "vtherm2": False, "vtherm1": False},
), ),
# More complex shedding # Un-shedding only (will be taken in reverse order)
( (
1000, 1000,
2000, 2000,
[ [
# already overpowering (non change) # should be not unshedded (too much power will be added)
{ {
"name": "vtherm1", "name": "vtherm1",
"device_power": 1100,
"is_device_active": False,
"is_over_climate": False,
"nb_underlying_entities": 1,
"on_percent": 1,
"is_overpowering_detected": True,
},
# already overpowering and already active (can be un overpowered)
{
"name": "vtherm2",
"device_power": 1100,
"is_device_active": True,
"is_over_climate": True,
"is_overpowering_detected": True,
},
# should terminate the overpowering
{
"name": "vtherm3",
"device_power": 800, "device_power": 800,
"is_device_active": False, "is_device_active": False,
"is_over_climate": False, "is_over_climate": False,
"nb_underlying_entities": 1, "nb_underlying_entities": 1,
"on_percent": 1, "on_percent": 1,
"is_overpowering_detected": True, "is_overpowering_detected": True,
"overpowering_state": STATE_ON,
}, },
# should terminate the overpowering and active # already stay unshedded cause already unshedded
{
"name": "vtherm4",
"device_power": 3800,
"is_device_active": True,
"is_over_climate": False,
"nb_underlying_entities": 1,
"on_percent": 1,
"is_overpowering_detected": True,
},
],
{"vtherm2": False, "vtherm3": False, "vtherm4": False},
),
# More complex shedding
(
1000,
2000,
[
# already overpowering (non change)
{
"name": "vtherm1",
"device_power": 1100,
"is_device_active": True,
"is_over_climate": False,
"nb_underlying_entities": 1,
"on_percent": 1,
"is_overpowering_detected": True,
},
# should be overpowering
{ {
"name": "vtherm2", "name": "vtherm2",
"device_power": 1800,
"is_device_active": False,
"is_over_climate": True,
"is_overpowering_detected": False,
},
# should terminate the overpowering and active but just before is overpowering
{
"name": "vtherm3",
"device_power": 100, "device_power": 100,
"is_device_active": True, "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, "is_over_climate": False,
"nb_underlying_entities": 1, "nb_underlying_entities": 1,
"on_percent": 1, "on_percent": 1,
"is_overpowering_detected": False, "is_overpowering_detected": True,
"overpowering_state": STATE_ON,
}, },
], ],
{"vtherm1": False, "vtherm2": True, "vtherm3": True}, {"vtherm4": False, "vtherm3": False},
), ),
# Sheeding only current_power > max_power (need to gain 1000 ) # Shedding
( (
2000, 2000,
1000, 1000,
@@ -432,36 +373,31 @@ async def test_central_power_manageer_find_vtherms(
"nb_underlying_entities": 1, "nb_underlying_entities": 1,
"on_percent": 1, "on_percent": 1,
"is_overpowering_detected": False, "is_overpowering_detected": False,
"overpowering_state": STATE_OFF,
}, },
# should be overpowering but is already # should be overpowering with many underlmying entities
{ {
"name": "vtherm2", "name": "vtherm2",
"device_power": 600, "device_power": 400,
"is_device_active": True, "is_device_active": True,
"is_over_climate": False, "is_over_climate": False,
"nb_underlying_entities": 4, "nb_underlying_entities": 4,
"on_percent": 0.1, "on_percent": 0.1,
"is_overpowering_detected": True, "is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
}, },
# over_climate should be not overpowering (device not active) # over_climate should be overpowering
{ {
"name": "vtherm3", "name": "vtherm3",
"device_power": 690, "device_power": 100,
"is_device_active": False,
"is_over_climate": True,
"is_overpowering_detected": False,
},
# over_climate should be overpowering (device active and not already overpowering)
{
"name": "vtherm4",
"device_power": 690,
"is_device_active": True, "is_device_active": True,
"is_over_climate": True, "is_over_climate": True,
"is_overpowering_detected": False, "is_overpowering_detected": False,
"overpowering_state": STATE_OFF,
}, },
# should not overpower (keep as is) # should pass cause not active
{ {
"name": "vtherm5", "name": "vtherm4",
"device_power": 800, "device_power": 800,
"is_device_active": False, "is_device_active": False,
"is_over_climate": False, "is_over_climate": False,
@@ -469,8 +405,39 @@ async def test_central_power_manageer_find_vtherms(
"on_percent": 1, "on_percent": 1,
"is_overpowering_detected": False, "is_overpowering_detected": False,
}, },
# should be not overpowering (already overpowering)
{
"name": "vtherm5",
"device_power": 400,
"is_device_active": True,
"is_over_climate": False,
"nb_underlying_entities": 4,
"on_percent": 0.1,
"is_overpowering_detected": True,
"overpowering_state": STATE_ON,
},
# should be overpowering with many underluying entities
{
"name": "vtherm6",
"device_power": 400,
"is_device_active": True,
"is_over_climate": False,
"nb_underlying_entities": 4,
"on_percent": 0.1,
"is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
},
# should not be overpowering (we have enough)
{
"name": "vtherm7",
"device_power": 1000,
"is_device_active": True,
"is_over_climate": True,
"is_overpowering_detected": False,
"overpowering_state": STATE_UNKNOWN,
},
], ],
{"vtherm1": True, "vtherm4": True}, {"vtherm1": True, "vtherm2": True, "vtherm3": True, "vtherm6": True},
), ),
], ],
) )
@@ -501,7 +468,10 @@ async def test_central_power_manageer_calculate_shedding(
vtherm.nb_underlying_entities = vtherm_config.get("nb_underlying_entities") vtherm.nb_underlying_entities = vtherm_config.get("nb_underlying_entities")
if not vtherm_config.get("is_over_climate"): if not vtherm_config.get("is_over_climate"):
vtherm.proportional_algorithm = MagicMock() vtherm.proportional_algorithm = MagicMock()
vtherm.proportional_algorithm.on_percent = vtherm_config.get("on_percent") vtherm.on_percent = vtherm.proportional_algorithm.on_percent = vtherm_config.get("on_percent")
else:
vtherm.on_percent = None
vtherm.proportional_algorithm = None
vtherm.power_manager = MagicMock(spec=FeaturePowerManager) vtherm.power_manager = MagicMock(spec=FeaturePowerManager)
vtherm.power_manager._vtherm = vtherm vtherm.power_manager._vtherm = vtherm
@@ -510,6 +480,7 @@ async def test_central_power_manageer_calculate_shedding(
"is_overpowering_detected" "is_overpowering_detected"
) )
vtherm.power_manager.device_power = vtherm_config.get("device_power") vtherm.power_manager.device_power = vtherm_config.get("device_power")
vtherm.power_manager.overpowering_state = vtherm_config.get("overpowering_state")
async def mock_set_overpowering( async def mock_set_overpowering(
overpowering, power_consumption_max=0, v=vtherm overpowering, power_consumption_max=0, v=vtherm
@@ -571,7 +542,7 @@ async def test_central_power_manager_power_event(
assert central_power_manager.power_temperature == 13 assert central_power_manager.power_temperature == 13
# 3. start listening (not really useful but don't eat bread) # 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 assert len(central_power_manager._active_listener) == 2
now: datetime = NowClass.get_now(hass) now: datetime = NowClass.get_now(hass)
@@ -600,6 +571,9 @@ async def test_central_power_manager_power_event(
"old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE), "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 expected_power = power if isinstance(power, (int, float)) else -999
assert central_power_manager.current_power == expected_power assert central_power_manager.current_power == expected_power
assert mock_calculate_shedding.call_count == nb_call assert mock_calculate_shedding.call_count == nb_call
@@ -621,8 +595,11 @@ async def test_central_power_manager_power_event(
"old_state": State("sensor.power_entity_id", STATE_UNAVAILABLE), "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 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( @pytest.mark.parametrize(
@@ -663,7 +640,7 @@ async def test_central_power_manager_max_power_event(
assert central_power_manager.power_temperature == 13 assert central_power_manager.power_temperature == 13
# 3. start listening (not really useful but don't eat bread) # 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 assert len(central_power_manager._active_listener) == 2
now: datetime = NowClass.get_now(hass) now: datetime = NowClass.get_now(hass)
@@ -694,6 +671,9 @@ async def test_central_power_manager_max_power_event(
"old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE), "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 expected_power = max_power if isinstance(max_power, (int, float)) else -999
assert central_power_manager.current_max_power == expected_power assert central_power_manager.current_max_power == expected_power
assert mock_calculate_shedding.call_count == nb_call assert mock_calculate_shedding.call_count == nb_call
@@ -715,5 +695,8 @@ async def test_central_power_manager_max_power_event(
"old_state": State("sensor.max_power_entity_id", STATE_UNAVAILABLE), "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 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

View File

@@ -90,7 +90,7 @@ async def test_motion_feature_manager_refresh(
assert custom_attributes["motion_off_delay_sec"] == 30 assert custom_attributes["motion_off_delay_sec"] == 30
# 3. start listening # 3. start listening
motion_manager.start_listening() await motion_manager.start_listening()
assert motion_manager.is_configured is True assert motion_manager.is_configured is True
assert motion_manager.motion_state == STATE_UNKNOWN assert motion_manager.motion_state == STATE_UNKNOWN
assert motion_manager.is_motion_detected is False 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, CONF_NO_MOTION_PRESET: PRESET_ECO,
} }
) )
motion_manager.start_listening() await motion_manager.start_listening()
# 2. test _motion_sensor_changed with the parametrized # 2. test _motion_sensor_changed with the parametrized
# fmt: off # fmt: off

View File

@@ -812,21 +812,20 @@ async def test_multiple_switch_power_management(
assert entity.power_manager.overpowering_state is STATE_OFF assert entity.power_manager.overpowering_state is STATE_OFF
# 2. Send power max mesurement too low and HVACMode is on # 2. Send power max mesurement too low and HVACMode is on
side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 74)) side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 49))
with patch( #fmt: off
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event" with patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
) as mock_send_event, patch( patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on" patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off, \
) as mock_heater_on, patch( patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off" #fmt: on
) as mock_heater_off:
now = now + timedelta(seconds=30) now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now) VersatileThermostatAPI.get_vtherm_api()._set_now(now)
assert entity.power_percent > 0 assert entity.power_percent > 0
# 100 of the device / 4 -> 25, current power 50 so max is 75 # 100 of the device / 4 -> 25, current power 50 so max is 75
await send_max_power_change_event(entity, 74, datetime.now()) await send_max_power_change_event(entity, 49, datetime.now())
assert entity.power_manager.is_overpowering_detected is True assert entity.power_manager.is_overpowering_detected is True
# All configuration is complete and power is > power_max we switch to POWER preset # All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_POWER assert entity.preset_mode is PRESET_POWER
@@ -843,7 +842,7 @@ async def test_multiple_switch_power_management(
"type": "start", "type": "start",
"current_power": 50, "current_power": 50,
"device_power": 100, "device_power": 100,
"current_max_power": 74, "current_max_power": 49,
"current_power_consumption": 100, "current_power_consumption": 100,
}, },
), ),

View File

@@ -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.is_configured is True
assert power_manager.overpowering_state == STATE_UNKNOWN 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 assert custom_attributes["current_max_power"] is None
# 3. start listening # 3. start listening
power_manager.start_listening() await power_manager.start_listening()
assert power_manager.is_configured is True assert power_manager.is_configured is True
assert power_manager.overpowering_state == STATE_UNKNOWN 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.is_configured is True
assert power_manager.overpowering_state == STATE_UNKNOWN assert power_manager.overpowering_state == STATE_UNKNOWN
@@ -517,17 +517,18 @@ async def test_power_management_hvac_on(
assert entity.power_manager.overpowering_state is STATE_OFF assert entity.power_manager.overpowering_state is STATE_OFF
# Send power max mesurement too low and HVACMode is on # Send power max mesurement too low and HVACMode is on
side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 149)) side_effects.add_or_update_side_effect("sensor.the_max_power_sensor", State("sensor.the_max_power_sensor", 49))
# fmt:off # fmt:off
with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \ 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, \ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \ patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_on") as mock_heater_on, \
patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off: patch("custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.turn_off") as mock_heater_off, \
patch("custom_components.versatile_thermostat.thermostat_switch.ThermostatOverSwitch.is_device_active", return_value="True"):
# fmt: on # fmt: on
now = now + timedelta(seconds=30) now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now) VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_max_power_change_event(entity, 149, datetime.now()) await send_max_power_change_event(entity, 49, now)
assert entity.power_manager.is_overpowering_detected is True assert entity.power_manager.is_overpowering_detected is True
# All configuration is complete and power is > power_max we switch to POWER preset # All configuration is complete and power is > power_max we switch to POWER preset
assert entity.preset_mode is PRESET_POWER assert entity.preset_mode is PRESET_POWER
@@ -544,7 +545,7 @@ async def test_power_management_hvac_on(
"type": "start", "type": "start",
"current_power": 50, "current_power": 50,
"device_power": 100, "device_power": 100,
"current_max_power": 149, "current_max_power": 49,
"current_power_consumption": 100.0, "current_power_consumption": 100.0,
}, },
), ),
@@ -554,8 +555,9 @@ async def test_power_management_hvac_on(
assert mock_heater_on.call_count == 0 assert mock_heater_on.call_count == 0
assert mock_heater_off.call_count == 1 assert mock_heater_off.call_count == 1
# Send power mesurement low to unseet power preset # 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_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 # fmt:off
with patch("homeassistant.core.StateMachine.get", side_effect=side_effects.get_side_effects()), \ 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, \ patch("custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event") as mock_send_event, \
@@ -565,7 +567,7 @@ async def test_power_management_hvac_on(
now = now + timedelta(seconds=30) now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now) VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_power_change_event(entity, 48, datetime.now()) await send_power_change_event(entity, 48, now)
assert entity.power_manager.is_overpowering_detected is False assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max, we restore previous preset # All configuration is complete and power is < power_max, we restore previous preset
assert entity.preset_mode is PRESET_BOOST assert entity.preset_mode is PRESET_BOOST

View File

@@ -75,7 +75,7 @@ async def test_presence_feature_manager(
assert custom_attributes["is_presence_configured"] is True assert custom_attributes["is_presence_configured"] is True
# 3. start listening # 3. start listening
presence_manager.start_listening() await presence_manager.start_listening()
assert presence_manager.is_configured is True assert presence_manager.is_configured is True
assert presence_manager.presence_state == STATE_UNKNOWN assert presence_manager.presence_state == STATE_UNKNOWN
assert presence_manager.is_absence_detected is False assert presence_manager.is_absence_detected is False

View File

@@ -170,7 +170,7 @@ async def test_window_feature_manager_refresh_sensor_action_turn_off(
) )
# 3. start listening # 3. start listening
window_manager.start_listening() await window_manager.start_listening()
assert window_manager.is_configured is True assert window_manager.is_configured is True
assert window_manager.window_state == STATE_UNKNOWN assert window_manager.window_state == STATE_UNKNOWN
assert window_manager.window_auto_state == STATE_UNAVAILABLE 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 # 3. start listening
window_manager.start_listening() await window_manager.start_listening()
assert window_manager.is_configured is True assert window_manager.is_configured is True
assert window_manager.window_state == STATE_UNKNOWN assert window_manager.window_state == STATE_UNKNOWN
assert window_manager.window_auto_state == STATE_UNAVAILABLE 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 # 3. start listening
window_manager.start_listening() await window_manager.start_listening()
assert len(window_manager._active_listener) == 1 assert len(window_manager._active_listener) == 1
# 4. test refresh with the parametrized # 4. test refresh with the parametrized
@@ -535,7 +535,7 @@ async def test_window_feature_manager_event_sensor_action_frost_only(
) )
# 3. start listening # 3. start listening
window_manager.start_listening() await window_manager.start_listening()
# 4. test refresh with the parametrized # 4. test refresh with the parametrized
# fmt:off # fmt:off
@@ -660,7 +660,7 @@ async def test_window_feature_manager_window_auto(
} }
) )
assert window_manager.is_window_auto_configured is True assert window_manager.is_window_auto_configured is True
window_manager.start_listening() await window_manager.start_listening()
# 2. Call manage window auto # 2. Call manage window auto
tz = get_tz(hass) # pylint: disable=invalid-name tz = get_tz(hass) # pylint: disable=invalid-name