Compare commits
2 Commits
6.8.4.beta
...
issue_662-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc7739d53b | ||
|
|
729f263cc8 |
@@ -57,13 +57,10 @@ class AutoStartStopDetectionAlgorithm:
|
||||
_accumulated_error: float = 0
|
||||
_error_threshold: float | None = None
|
||||
_last_calculation_date: datetime | None = None
|
||||
_last_switch_date: datetime | None = None
|
||||
|
||||
def __init__(self, level: TYPE_AUTO_START_STOP_LEVELS, vtherm_name) -> None:
|
||||
"""Initalize a new algorithm with the right constants"""
|
||||
self._vtherm_name = vtherm_name
|
||||
self._last_calculation_date = None
|
||||
self._last_switch_date = None
|
||||
self._init_level(level)
|
||||
|
||||
def _init_level(self, level: TYPE_AUTO_START_STOP_LEVELS):
|
||||
@@ -146,26 +143,17 @@ class AutoStartStopDetectionAlgorithm:
|
||||
|
||||
temp_at_dt = current_temp + slope_min * self._dt
|
||||
|
||||
# Calculate the number of minute from last_switch
|
||||
nb_minutes_since_last_switch = 999
|
||||
if self._last_switch_date is not None:
|
||||
nb_minutes_since_last_switch = (
|
||||
now - self._last_switch_date
|
||||
).total_seconds() / 60
|
||||
|
||||
# Check to turn-off
|
||||
# When we hit the threshold, that mean we can turn off
|
||||
if hvac_mode == HVACMode.HEAT:
|
||||
if (
|
||||
self._accumulated_error <= -self._error_threshold
|
||||
and temp_at_dt >= target_temp + TEMP_HYSTERESIS
|
||||
and nb_minutes_since_last_switch >= self._dt
|
||||
):
|
||||
_LOGGER.info(
|
||||
"%s - We need to stop, there is no need for heating for a long time.",
|
||||
self,
|
||||
)
|
||||
self._last_switch_date = now
|
||||
return AUTO_START_STOP_ACTION_OFF
|
||||
else:
|
||||
_LOGGER.debug("%s - nothing to do, we are heating", self)
|
||||
@@ -175,13 +163,11 @@ class AutoStartStopDetectionAlgorithm:
|
||||
if (
|
||||
self._accumulated_error >= self._error_threshold
|
||||
and temp_at_dt <= target_temp - TEMP_HYSTERESIS
|
||||
and nb_minutes_since_last_switch >= self._dt
|
||||
):
|
||||
_LOGGER.info(
|
||||
"%s - We need to stop, there is no need for cooling for a long time.",
|
||||
self,
|
||||
)
|
||||
self._last_switch_date = now
|
||||
return AUTO_START_STOP_ACTION_OFF
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
@@ -192,15 +178,11 @@ class AutoStartStopDetectionAlgorithm:
|
||||
|
||||
# check to turn on
|
||||
if hvac_mode == HVACMode.OFF and saved_hvac_mode == HVACMode.HEAT:
|
||||
if (
|
||||
temp_at_dt <= target_temp - TEMP_HYSTERESIS
|
||||
and nb_minutes_since_last_switch >= self._dt
|
||||
):
|
||||
if temp_at_dt <= target_temp - TEMP_HYSTERESIS:
|
||||
_LOGGER.info(
|
||||
"%s - We need to start, because it will be time to heat",
|
||||
self,
|
||||
)
|
||||
self._last_switch_date = now
|
||||
return AUTO_START_STOP_ACTION_ON
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
@@ -210,15 +192,11 @@ class AutoStartStopDetectionAlgorithm:
|
||||
return AUTO_START_STOP_ACTION_NOTHING
|
||||
|
||||
if hvac_mode == HVACMode.OFF and saved_hvac_mode == HVACMode.COOL:
|
||||
if (
|
||||
temp_at_dt >= target_temp + TEMP_HYSTERESIS
|
||||
and nb_minutes_since_last_switch >= self._dt
|
||||
):
|
||||
if temp_at_dt >= target_temp + TEMP_HYSTERESIS:
|
||||
_LOGGER.info(
|
||||
"%s - We need to start, because it will be time to cool",
|
||||
self,
|
||||
)
|
||||
self._last_switch_date = now
|
||||
return AUTO_START_STOP_ACTION_ON
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
@@ -257,10 +235,5 @@ class AutoStartStopDetectionAlgorithm:
|
||||
"""Get the level value"""
|
||||
return self._level
|
||||
|
||||
@property
|
||||
def last_switch_date(self) -> datetime | None:
|
||||
"""Get the last of the last switch"""
|
||||
return self._last_switch_date
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"AutoStartStopDetectionAlgorithm-{self._vtherm_name}"
|
||||
|
||||
@@ -82,10 +82,6 @@ T = TypeVar("T", bound=UnderlyingEntity)
|
||||
class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
||||
"""Representation of a base class for all Versatile Thermostat device."""
|
||||
|
||||
# breaking change with 2024.12 climate workaround
|
||||
_attr_swing_horizontal_modes = []
|
||||
_attr_swing_horizontal_mode = ""
|
||||
|
||||
_entity_component_unrecorded_attributes = (
|
||||
ClimateEntity._entity_component_unrecorded_attributes.union(
|
||||
frozenset(
|
||||
@@ -131,7 +127,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
||||
"max_power_sensor_entity_id",
|
||||
"temperature_unit",
|
||||
"is_device_active",
|
||||
"device_actives",
|
||||
"nb_device_actives",
|
||||
"target_temperature_step",
|
||||
"is_used_by_central_boiler",
|
||||
"temperature_slope",
|
||||
@@ -1000,19 +996,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def device_actives(self) -> int:
|
||||
"""Calculate the active devices"""
|
||||
ret = []
|
||||
for under in self._underlyings:
|
||||
if under.is_device_active:
|
||||
ret.append(under.entity_id)
|
||||
return ret
|
||||
|
||||
@property
|
||||
def nb_device_actives(self) -> int:
|
||||
"""Calculate the number of active devices"""
|
||||
return len(self.device_actives)
|
||||
ret = 0
|
||||
for under in self._underlyings:
|
||||
if under.is_device_active:
|
||||
ret += 1
|
||||
return ret
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
@@ -2685,7 +2676,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
||||
"timezone": str(self._current_tz),
|
||||
"temperature_unit": self.temperature_unit,
|
||||
"is_device_active": self.is_device_active,
|
||||
"device_actives": self.device_actives,
|
||||
"nb_device_actives": self.nb_device_actives,
|
||||
"ema_temp": self._ema_temp,
|
||||
"is_used_by_central_boiler": self.is_used_by_central_boiler,
|
||||
|
||||
@@ -118,7 +118,7 @@ async def async_setup_entry(
|
||||
SERVICE_SET_AUTO_REGULATION_MODE,
|
||||
{
|
||||
vol.Required("auto_regulation_mode"): vol.In(
|
||||
["None", "Light", "Medium", "Strong", "Slow", "Expert"]
|
||||
["None", "Light", "Medium", "Strong", "Slow"]
|
||||
),
|
||||
},
|
||||
"service_set_auto_regulation_mode",
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"quality_scale": "silver",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"version": "6.8.3",
|
||||
"version": "6.8.0",
|
||||
"zeroconf": []
|
||||
}
|
||||
@@ -644,10 +644,6 @@ class NbActiveDeviceForBoilerSensor(SensorEntity):
|
||||
"""Representation of the threshold of the number of VTherm
|
||||
which should be active to activate the boiler"""
|
||||
|
||||
_entity_component_unrecorded_attributes = SensorEntity._entity_component_unrecorded_attributes.union( # pylint: disable=protected-access
|
||||
frozenset({"active_device_ids"})
|
||||
)
|
||||
|
||||
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||
"""Initialize the energy sensor"""
|
||||
self._hass = hass
|
||||
@@ -657,14 +653,6 @@ class NbActiveDeviceForBoilerSensor(SensorEntity):
|
||||
self._attr_unique_id = "nb_device_active_boiler"
|
||||
self._attr_value = self._attr_native_value = None # default value
|
||||
self._entities = []
|
||||
self._attr_active_device_ids = [] # Holds the entity ids of active devices
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict:
|
||||
"""Return additional attributes for the sensor."""
|
||||
return {
|
||||
"active_device_ids": self._attr_active_device_ids,
|
||||
}
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
@@ -730,19 +718,19 @@ class NbActiveDeviceForBoilerSensor(SensorEntity):
|
||||
self.calculate_nb_active_devices,
|
||||
)
|
||||
_LOGGER.info(
|
||||
"%s - the underlyings that could control the central boiler are %s",
|
||||
"%s - the underlyings that could controls the central boiler are %s",
|
||||
self,
|
||||
underlying_entities_id,
|
||||
)
|
||||
self.async_on_remove(listener_cancel)
|
||||
else:
|
||||
_LOGGER.debug("%s - no VTherm could control the central boiler", self)
|
||||
_LOGGER.debug("%s - no VTherm could controls the central boiler", self)
|
||||
|
||||
await self.calculate_nb_active_devices(None)
|
||||
|
||||
async def calculate_nb_active_devices(self, event: Event):
|
||||
"""Calculate the number of active VTherm that have an
|
||||
influence on the central boiler and update the list of active device names."""
|
||||
influence on central boiler"""
|
||||
|
||||
# _LOGGER.debug("%s- calculate_nb_active_devices - the event is %s ", self, event)
|
||||
|
||||
@@ -769,8 +757,6 @@ class NbActiveDeviceForBoilerSensor(SensorEntity):
|
||||
old_state is not None
|
||||
and new_state.state == old_state.state
|
||||
and new_hvac_action == old_hvac_action
|
||||
# issue 698 - force recalculation when underlying climate doesn't have any hvac_action
|
||||
and new_hvac_action is not None
|
||||
):
|
||||
# A false state change
|
||||
return
|
||||
@@ -788,28 +774,20 @@ class NbActiveDeviceForBoilerSensor(SensorEntity):
|
||||
)
|
||||
|
||||
nb_active = 0
|
||||
active_device_ids = []
|
||||
|
||||
for entity in self._entities:
|
||||
device_actives = entity.device_actives
|
||||
nb_active += entity.nb_device_actives
|
||||
_LOGGER.debug(
|
||||
"After examining the hvac_action of %s, device_actives is %s",
|
||||
"After examining the hvac_action of %s, nb_active is %s",
|
||||
entity.name,
|
||||
device_actives,
|
||||
nb_active,
|
||||
)
|
||||
|
||||
nb_active += len(device_actives)
|
||||
active_device_ids.extend(device_actives)
|
||||
|
||||
self._attr_native_value = nb_active
|
||||
self._attr_active_device_ids = active_device_ids
|
||||
_LOGGER.debug(
|
||||
"%s - Number of active underlying entities is %s", self, nb_active
|
||||
)
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def active_device_ids(self) -> list:
|
||||
"""Get the list of active device id"""
|
||||
return self._attr_active_device_ids
|
||||
|
||||
def __str__(self):
|
||||
return f"VersatileThermostat-{self.name}"
|
||||
|
||||
@@ -60,7 +60,6 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
||||
"auto_start_stop_enable",
|
||||
"auto_start_stop_accumulated_error",
|
||||
"auto_start_stop_accumulated_error_threshold",
|
||||
"auto_start_stop_last_switch_date",
|
||||
"follow_underlying_temp_change",
|
||||
}
|
||||
)
|
||||
@@ -183,8 +182,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
||||
await super()._async_internal_set_temperature(temperature)
|
||||
|
||||
self._regulation_algo.set_target_temp(self.target_temperature)
|
||||
# Is necessary cause control_heating method will not force the update.
|
||||
await self._send_regulated_temperature(force=True)
|
||||
# is done by control_heating method. No need to do it here
|
||||
# await self._send_regulated_temperature(force=True)
|
||||
|
||||
async def _send_regulated_temperature(self, force=False):
|
||||
"""Sends the regulated temperature to all underlying"""
|
||||
@@ -556,10 +555,6 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
||||
"auto_start_stop_accumulated_error_threshold"
|
||||
] = self._auto_start_stop_algo.accumulated_error_threshold
|
||||
|
||||
self._attr_extra_state_attributes["auto_start_stop_last_switch_date"] = (
|
||||
self._auto_start_stop_algo.last_switch_date
|
||||
)
|
||||
|
||||
self._attr_extra_state_attributes["follow_underlying_temp_change"] = (
|
||||
self._follow_underlying_temp_change
|
||||
)
|
||||
@@ -1119,6 +1114,15 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
||||
|
||||
return self._support_flags
|
||||
|
||||
# We keep the step configured for the VTherm and not the step of the underlying
|
||||
# @property
|
||||
# def target_temperature_step(self) -> float | None:
|
||||
# """Return the supported step of target temperature."""
|
||||
# if self.underlying_entity(0):
|
||||
# return self.underlying_entity(0).target_temperature_step
|
||||
#
|
||||
# return None
|
||||
|
||||
@property
|
||||
def target_temperature_high(self) -> float | None:
|
||||
"""Return the highbound target temperature we try to reach.
|
||||
|
||||
@@ -277,15 +277,12 @@ class ThermostatOverClimateValve(ThermostatOverClimate):
|
||||
return self.valve_open_percent > 0
|
||||
|
||||
@property
|
||||
def device_actives(self) -> int:
|
||||
def nb_device_actives(self) -> int:
|
||||
"""Calculate the number of active devices"""
|
||||
if self.is_device_active:
|
||||
return [
|
||||
under.opening_degree_entity_id
|
||||
for under in self._underlyings_valve_regulation
|
||||
]
|
||||
return len(self._underlyings_valve_regulation)
|
||||
else:
|
||||
return []
|
||||
return 0
|
||||
|
||||
@property
|
||||
def activable_underlying_entities(self) -> list | None:
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
}
|
||||
},
|
||||
"valve_regulation": {
|
||||
"title": "Auto-régulation par vanne",
|
||||
"title": "Auto-régulation par vanne - {name}",
|
||||
"description": "Configuration de l'auto-régulation par controle direct de la vanne",
|
||||
"data": {
|
||||
"offset_calibration_entity_ids": "Entités de 'calibrage du décalage''",
|
||||
@@ -257,7 +257,7 @@
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu",
|
||||
"title": "Menu - {name}",
|
||||
"description": "Paramétrez votre thermostat. Vous pourrez finaliser la configuration quand tous les paramètres auront été saisis.",
|
||||
"menu_options": {
|
||||
"main": "Principaux Attributs",
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"content_in_root": false,
|
||||
"render_readme": true,
|
||||
"hide_default_branch": false,
|
||||
"homeassistant": "2024.12.3"
|
||||
"homeassistant": "2024.10.4"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
homeassistant==2024.12.3
|
||||
homeassistant==2024.10.4
|
||||
|
||||
@@ -579,7 +579,6 @@ class MockNumber(NumberEntity):
|
||||
def set_native_value(self, value: float):
|
||||
"""Change the value"""
|
||||
self._attr_native_value = value
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
async def create_thermostat(
|
||||
|
||||
@@ -309,7 +309,7 @@ async def test_over_climate_regulation_limitations(
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
|
||||
# the regulated temperature will not change because when we set temp manually it is forced
|
||||
assert entity.regulated_target_temp == 19.5
|
||||
assert entity.regulated_target_temp == 17 # 19.5
|
||||
|
||||
# 2. set manual target temp (at now - 18) -> the regulation should be taken into account
|
||||
event_timestamp = now - timedelta(minutes=18)
|
||||
|
||||
@@ -15,7 +15,6 @@ from custom_components.versatile_thermostat.auto_start_stop_algorithm import (
|
||||
AutoStartStopDetectionAlgorithm,
|
||||
AUTO_START_STOP_ACTION_NOTHING,
|
||||
AUTO_START_STOP_ACTION_OFF,
|
||||
AUTO_START_STOP_ACTION_ON,
|
||||
)
|
||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
@@ -45,7 +44,6 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.accumulated_error == -1
|
||||
assert algo.last_switch_date is None
|
||||
|
||||
# 2. should not stop (accumulated_error too low)
|
||||
now = now + timedelta(minutes=5)
|
||||
@@ -59,7 +57,6 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.accumulated_error == -6
|
||||
assert algo.last_switch_date is None
|
||||
|
||||
# 3. should not stop (accumulated_error too low)
|
||||
now = now + timedelta(minutes=2)
|
||||
@@ -73,7 +70,6 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert algo.accumulated_error == -8
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.last_switch_date is None
|
||||
|
||||
# 4 .No change on accumulated error because the new measure is too near the last one
|
||||
now = now + timedelta(seconds=11)
|
||||
@@ -87,7 +83,6 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert algo.accumulated_error == -8
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.last_switch_date is None
|
||||
|
||||
# 5. should stop now because accumulated_error is > ERROR_THRESHOLD for slow (10)
|
||||
now = now + timedelta(minutes=4)
|
||||
@@ -101,9 +96,6 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert algo.accumulated_error == -10
|
||||
assert ret == AUTO_START_STOP_ACTION_OFF
|
||||
assert algo.last_switch_date is not None
|
||||
assert algo.last_switch_date == now
|
||||
last_now = now
|
||||
|
||||
# 6. inverse the temperature (target > current) -> accumulated_error should be divided by 2
|
||||
now = now + timedelta(minutes=2)
|
||||
@@ -117,111 +109,14 @@ async def test_auto_start_stop_algo_slow_heat_off(hass: HomeAssistant):
|
||||
)
|
||||
assert algo.accumulated_error == -4 # -10/2 + 1
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.last_switch_date == last_now
|
||||
|
||||
# 7. change level to slow (no real change) -> error_accumulated should not reset to 0
|
||||
algo.set_level(AUTO_START_STOP_LEVEL_SLOW)
|
||||
assert algo.accumulated_error == -4
|
||||
assert algo.last_switch_date == last_now
|
||||
|
||||
# 8. change level -> error_accumulated should reset to 0
|
||||
algo.set_level(AUTO_START_STOP_LEVEL_FAST)
|
||||
assert algo.accumulated_error == 0
|
||||
assert algo.last_switch_date == last_now
|
||||
|
||||
|
||||
async def test_auto_start_stop_too_fast_change(hass: HomeAssistant):
|
||||
"""Testing directly the algorithm in Slow level"""
|
||||
algo: AutoStartStopDetectionAlgorithm = AutoStartStopDetectionAlgorithm(
|
||||
AUTO_START_STOP_LEVEL_SLOW, "testu"
|
||||
)
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
assert algo._dt == 30
|
||||
assert algo._vtherm_name == "testu"
|
||||
|
||||
#
|
||||
# Testing with turn_on
|
||||
#
|
||||
|
||||
# 1. should stop
|
||||
algo._accumulated_error = -100
|
||||
ret = algo.calculate_action(
|
||||
hvac_mode=HVACMode.HEAT,
|
||||
saved_hvac_mode=HVACMode.OFF,
|
||||
target_temp=10,
|
||||
current_temp=21,
|
||||
slope_min=0.5,
|
||||
now=now,
|
||||
)
|
||||
|
||||
assert ret == AUTO_START_STOP_ACTION_OFF
|
||||
assert algo.last_switch_date is not None
|
||||
assert algo.last_switch_date == now
|
||||
last_now = now
|
||||
|
||||
# 2. now we should turn on but to near the last change -> no nothing to do
|
||||
now = now + timedelta(minutes=2)
|
||||
algo._accumulated_error = -100
|
||||
ret = algo.calculate_action(
|
||||
hvac_mode=HVACMode.OFF,
|
||||
saved_hvac_mode=HVACMode.HEAT,
|
||||
target_temp=21,
|
||||
current_temp=17,
|
||||
slope_min=-0.1,
|
||||
now=now,
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.last_switch_date == last_now
|
||||
|
||||
# 3. now we should turn on and now is much later ->
|
||||
now = now + timedelta(minutes=30)
|
||||
algo._accumulated_error = -100
|
||||
ret = algo.calculate_action(
|
||||
hvac_mode=HVACMode.OFF,
|
||||
saved_hvac_mode=HVACMode.HEAT,
|
||||
target_temp=21,
|
||||
current_temp=17,
|
||||
slope_min=-0.1,
|
||||
now=now,
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_ON
|
||||
assert algo.last_switch_date == now
|
||||
last_now = now
|
||||
|
||||
#
|
||||
# Testing with turn_off
|
||||
#
|
||||
|
||||
# 4. try to turn_off but too speed (29 min)
|
||||
now = now + timedelta(minutes=29)
|
||||
algo._accumulated_error = -100
|
||||
ret = algo.calculate_action(
|
||||
hvac_mode=HVACMode.HEAT,
|
||||
saved_hvac_mode=HVACMode.OFF,
|
||||
target_temp=17,
|
||||
current_temp=21,
|
||||
slope_min=0.5,
|
||||
now=now,
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_NOTHING
|
||||
assert algo.last_switch_date == last_now
|
||||
|
||||
# 5. turn_off much later (29 min + 1 min)
|
||||
now = now + timedelta(minutes=1)
|
||||
algo._accumulated_error = -100
|
||||
ret = algo.calculate_action(
|
||||
hvac_mode=HVACMode.HEAT,
|
||||
saved_hvac_mode=HVACMode.OFF,
|
||||
target_temp=17,
|
||||
current_temp=21,
|
||||
slope_min=0.5,
|
||||
now=now,
|
||||
)
|
||||
assert ret == AUTO_START_STOP_ACTION_OFF
|
||||
assert algo.last_switch_date == now
|
||||
|
||||
|
||||
async def test_auto_start_stop_algo_medium_cool_off(hass: HomeAssistant):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
""" Test the central_configuration """
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
|
||||
from unittest.mock import patch, call
|
||||
|
||||
@@ -29,8 +29,6 @@ from custom_components.versatile_thermostat.binary_sensor import (
|
||||
CentralBoilerBinarySensor,
|
||||
)
|
||||
|
||||
from custom_components.versatile_thermostat.sensor import NbActiveDeviceForBoilerSensor
|
||||
|
||||
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
from .const import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
|
||||
@@ -105,7 +103,7 @@ async def test_update_central_boiler_state_simple(
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_UNDERLYING_LIST: [switch1.entity_id],
|
||||
CONF_HEATER: switch1.entity_id,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
@@ -149,13 +147,6 @@ async def test_update_central_boiler_state_simple(
|
||||
assert boiler_binary_sensor is not None
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
nb_device_active_sensor: NbActiveDeviceForBoilerSensor = search_entity(
|
||||
hass, "sensor.nb_device_active_for_boiler", "sensor"
|
||||
)
|
||||
assert nb_device_active_sensor is not None
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
# 1. start a heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -204,9 +195,6 @@ async def test_update_central_boiler_state_simple(
|
||||
assert api.nb_active_device_for_boiler == 1
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 1
|
||||
assert nb_device_active_sensor.active_device_ids == ["switch.switch1"]
|
||||
|
||||
# 2. stop a heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -247,9 +235,6 @@ async def test_update_central_boiler_state_simple(
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
@@ -287,12 +272,10 @@ async def test_update_central_boiler_state_multiple(
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_UNDERLYING_LIST: [
|
||||
switch1.entity_id,
|
||||
switch2.entity_id,
|
||||
switch3.entity_id,
|
||||
switch4.entity_id,
|
||||
],
|
||||
CONF_HEATER: switch1.entity_id,
|
||||
CONF_HEATER_2: switch2.entity_id,
|
||||
CONF_HEATER_3: switch3.entity_id,
|
||||
CONF_HEATER_4: switch4.entity_id,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
@@ -319,18 +302,10 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert entity.underlying_entities[1].entity_id == "switch.switch2"
|
||||
assert entity.underlying_entities[2].entity_id == "switch.switch3"
|
||||
assert entity.underlying_entities[3].entity_id == "switch.switch4"
|
||||
assert entity.device_actives == []
|
||||
assert entity.nb_device_actives == 0
|
||||
|
||||
assert api.nb_active_device_for_boiler_threshold == 1
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
|
||||
nb_device_active_sensor: NbActiveDeviceForBoilerSensor = search_entity(
|
||||
hass, "sensor.nb_device_active_for_boiler", "sensor"
|
||||
)
|
||||
assert nb_device_active_sensor is not None
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
# Force the VTherm to heat
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
@@ -363,7 +338,7 @@ async def test_update_central_boiler_state_multiple(
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.device_actives == ["switch.switch1"]
|
||||
assert entity.nb_device_actives == 1
|
||||
|
||||
assert mock_service_call.call_count == 1
|
||||
# No switch of the boiler
|
||||
@@ -381,9 +356,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 1
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 1
|
||||
assert nb_device_active_sensor.active_device_ids == ["switch.switch1"]
|
||||
|
||||
# 2. start a 2nd heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -396,7 +368,7 @@ async def test_update_central_boiler_state_multiple(
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.device_actives == ["switch.switch1", "switch.switch2"]
|
||||
assert entity.nb_device_actives == 2
|
||||
|
||||
# Only the first heater is started by the algo
|
||||
assert mock_service_call.call_count == 1
|
||||
@@ -416,12 +388,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 2
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 2
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"switch.switch1",
|
||||
"switch.switch2",
|
||||
]
|
||||
|
||||
# 3. start a 3rd heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -470,13 +436,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 3
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 3
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"switch.switch1",
|
||||
"switch.switch2",
|
||||
"switch.switch3",
|
||||
]
|
||||
|
||||
# 4. start a 4th heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -507,14 +466,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 4
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 4
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"switch.switch1",
|
||||
"switch.switch2",
|
||||
"switch.switch3",
|
||||
"switch.switch4",
|
||||
]
|
||||
|
||||
# 5. stop a heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -533,13 +484,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 3
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 3
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"switch.switch2",
|
||||
"switch.switch3",
|
||||
"switch.switch4",
|
||||
]
|
||||
|
||||
# 6. stop a 2nd heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -580,12 +524,6 @@ async def test_update_central_boiler_state_multiple(
|
||||
assert api.nb_active_device_for_boiler == 2
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 2
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"switch.switch2",
|
||||
"switch.switch3",
|
||||
]
|
||||
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
@@ -620,7 +558,7 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_UNDERLYING_LIST: [valve1.entity_id],
|
||||
CONF_VALVE: valve1.entity_id,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_INVERSE_SWITCH: False,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
@@ -656,7 +594,7 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
assert entity.hvac_mode == HVACMode.HEAT
|
||||
assert entity.device_actives == []
|
||||
assert entity.nb_device_actives == 0
|
||||
|
||||
boiler_binary_sensor: CentralBoilerBinarySensor = search_entity(
|
||||
hass, "binary_sensor.central_boiler", "binary_sensor"
|
||||
@@ -664,13 +602,6 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
assert boiler_binary_sensor is not None
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
nb_device_active_sensor: NbActiveDeviceForBoilerSensor = search_entity(
|
||||
hass, "sensor.nb_device_active_for_boiler", "sensor"
|
||||
)
|
||||
assert nb_device_active_sensor is not None
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
# 1. start a valve
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -685,7 +616,7 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.device_actives == ["number.valve1"]
|
||||
assert entity.nb_device_actives == 1
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
@@ -713,11 +644,6 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
assert api.nb_active_device_for_boiler == 1
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 1
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"number.valve1",
|
||||
]
|
||||
|
||||
# 2. stop a heater
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -732,7 +658,7 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
assert entity.hvac_action == HVACAction.IDLE
|
||||
assert entity.device_actives == []
|
||||
assert entity.nb_device_actives == 0
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
@@ -761,9 +687,6 @@ async def test_update_central_boiler_state_simple_valve(
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
@@ -798,7 +721,7 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_UNDERLYING_LIST: [climate1.entity_id],
|
||||
CONF_CLIMATE: climate1.entity_id,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
@@ -825,13 +748,6 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
assert api.nb_active_device_for_boiler_threshold == 1
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
|
||||
nb_device_active_sensor: NbActiveDeviceForBoilerSensor = search_entity(
|
||||
hass, "sensor.nb_device_active_for_boiler", "sensor"
|
||||
)
|
||||
assert nb_device_active_sensor is not None
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
# Force the VTherm to heat
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
@@ -840,7 +756,7 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
assert entity.hvac_mode == HVACMode.HEAT
|
||||
assert entity.device_actives == []
|
||||
assert entity.nb_device_actives == 0
|
||||
|
||||
boiler_binary_sensor: CentralBoilerBinarySensor = search_entity(
|
||||
hass, "binary_sensor.central_boiler", "binary_sensor"
|
||||
@@ -863,7 +779,7 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.device_actives == ["climate.climate1"]
|
||||
assert entity.nb_device_actives == 1
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
@@ -891,11 +807,6 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
assert api.nb_active_device_for_boiler == 1
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 1
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"climate.climate1",
|
||||
]
|
||||
|
||||
# 2. stop a climate
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
@@ -910,7 +821,7 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
assert entity.hvac_action == HVACAction.IDLE
|
||||
assert entity.device_actives == []
|
||||
assert entity.nb_device_actives == 0
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
@@ -939,277 +850,6 @@ async def test_update_central_boiler_state_simple_climate(
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
async def test_update_central_boiler_state_simple_climate_valve_regulation(
|
||||
hass: HomeAssistant,
|
||||
# skip_hass_states_is_state,
|
||||
# skip_hass_states_get,
|
||||
init_central_config_with_boiler_fixture,
|
||||
):
|
||||
"""Test that the central boiler state behavior with a climate with valve regulation"""
|
||||
|
||||
api = VersatileThermostatAPI.get_vtherm_api(hass)
|
||||
|
||||
climate1 = MockClimate(hass, "climate1", "theClimate1")
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverClimateMockName",
|
||||
unique_id="uniqueId",
|
||||
data={
|
||||
CONF_NAME: "TheOverClimateMockName",
|
||||
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||
CONF_CYCLE_MIN: 5,
|
||||
CONF_TEMP_MIN: 8,
|
||||
CONF_TEMP_MAX: 18,
|
||||
"frost_temp": 10,
|
||||
"eco_temp": 17,
|
||||
"comfort_temp": 18,
|
||||
"boost_temp": 21,
|
||||
CONF_USE_WINDOW_FEATURE: False,
|
||||
CONF_USE_MOTION_FEATURE: False,
|
||||
CONF_USE_POWER_FEATURE: False,
|
||||
CONF_USE_PRESENCE_FEATURE: False,
|
||||
CONF_UNDERLYING_LIST: [climate1.entity_id],
|
||||
CONF_OPENING_DEGREE_LIST: ["number.mock_opening_degree"],
|
||||
CONF_CLOSING_DEGREE_LIST: [],
|
||||
CONF_OFFSET_CALIBRATION_LIST: [],
|
||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_VALVE,
|
||||
CONF_AUTO_REGULATION_DTEMP: 0,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN: 0,
|
||||
CONF_TPI_COEF_INT: 0.3,
|
||||
CONF_TPI_COEF_EXT: 0.1,
|
||||
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
|
||||
CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_HIGH,
|
||||
CONF_AUTO_REGULATION_USE_DEVICE_TEMP: False,
|
||||
CONF_MINIMAL_ACTIVATION_DELAY: 30,
|
||||
CONF_SECURITY_DELAY_MIN: 5,
|
||||
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
|
||||
CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1,
|
||||
CONF_USE_MAIN_CENTRAL_CONFIG: True,
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG: True,
|
||||
CONF_USE_ADVANCED_CENTRAL_CONFIG: True,
|
||||
CONF_USED_BY_CENTRAL_BOILER: True,
|
||||
},
|
||||
)
|
||||
|
||||
open_degree_entity = MockNumber(hass, "mock_opening_degree", "Opening degree")
|
||||
open_degree_entity.set_native_value(0)
|
||||
|
||||
# mock_get_state will be called for each OPENING/CLOSING/OFFSET_CALIBRATION list
|
||||
mock_get_state_side_effect = SideEffects(
|
||||
{
|
||||
open_degree_entity.entity_id: State(
|
||||
open_degree_entity.entity_id,
|
||||
open_degree_entity.state,
|
||||
{"min": 0, "max": 100},
|
||||
),
|
||||
"number.mock_closing_degree": State(
|
||||
"number.mock_closing_degree", "0", {"min": 0, "max": 100}
|
||||
),
|
||||
"number.mock_offset_calibration": State(
|
||||
"number.mock_offset_calibration", "0", {"min": -12, "max": 12}
|
||||
),
|
||||
},
|
||||
State("unknown.entity_id", "unknown"),
|
||||
)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||
return_value=climate1,
|
||||
), patch(
|
||||
"homeassistant.core.StateMachine.get",
|
||||
side_effect=mock_get_state_side_effect.get_side_effects(),
|
||||
):
|
||||
entity: ThermostatOverClimate = await create_thermostat(
|
||||
hass, entry, "climate.theoverclimatemockname"
|
||||
)
|
||||
assert entity
|
||||
assert entity.name == "TheOverClimateMockName"
|
||||
assert entity.is_over_climate
|
||||
assert entity.underlying_entities[0].entity_id == "climate.climate1"
|
||||
|
||||
assert api.nb_active_device_for_boiler_threshold == 1
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
|
||||
nb_device_active_sensor: NbActiveDeviceForBoilerSensor = search_entity(
|
||||
hass, "sensor.nb_device_active_for_boiler", "sensor"
|
||||
)
|
||||
assert nb_device_active_sensor is not None
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
# Force the VTherm to heat
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
entity._set_now(now)
|
||||
|
||||
await send_temperature_change_event(entity, 30, now)
|
||||
await send_ext_temperature_change_event(entity, 30, now)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
await entity.async_set_preset_mode(PRESET_BOOST)
|
||||
|
||||
# the VTherm should not heat now
|
||||
assert entity.hvac_mode == HVACMode.HEAT
|
||||
assert entity.hvac_action == HVACAction.OFF
|
||||
assert entity.activable_underlying_entities[0]._percent_open == 0
|
||||
assert entity.device_actives == []
|
||||
|
||||
boiler_binary_sensor: CentralBoilerBinarySensor = search_entity(
|
||||
hass, "binary_sensor.central_boiler", "binary_sensor"
|
||||
)
|
||||
assert boiler_binary_sensor is not None
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
# 1. start a climate
|
||||
open_degree_entity.set_native_value(100)
|
||||
mock_get_state_side_effect = SideEffects(
|
||||
{
|
||||
open_degree_entity.entity_id: State(
|
||||
open_degree_entity.entity_id,
|
||||
open_degree_entity.state,
|
||||
{"min": 0, "max": 100},
|
||||
),
|
||||
"number.mock_closing_degree": State(
|
||||
"number.mock_closing_degree", "0", {"min": 0, "max": 100}
|
||||
),
|
||||
"number.mock_offset_calibration": State(
|
||||
"number.mock_offset_calibration", "0", {"min": -12, "max": 12}
|
||||
),
|
||||
},
|
||||
State("unknown.entity_id", "unknown"),
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call, patch(
|
||||
"custom_components.versatile_thermostat.binary_sensor.send_vtherm_event"
|
||||
) as mock_send_event, patch(
|
||||
"homeassistant.core.StateMachine.get",
|
||||
side_effect=mock_get_state_side_effect.get_side_effects(),
|
||||
):
|
||||
now = now + timedelta(minutes=1)
|
||||
entity._set_now(now)
|
||||
|
||||
await send_temperature_change_event(entity, 10, now)
|
||||
# we have to simulate the climate also else the test don't work
|
||||
climate1.set_hvac_mode(HVACMode.HEAT)
|
||||
climate1.set_hvac_action(HVACAction.HEATING)
|
||||
climate1.async_write_ha_state()
|
||||
open_degree_entity.set_native_value(100)
|
||||
# Wait for state event propagation
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.device_actives == ["number.mock_opening_degree"]
|
||||
|
||||
assert api.nb_active_device_for_boiler == 1
|
||||
assert boiler_binary_sensor.state == STATE_ON
|
||||
|
||||
assert nb_device_active_sensor.state == 1
|
||||
assert nb_device_active_sensor.active_device_ids == [
|
||||
"number.mock_opening_degree",
|
||||
]
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call.service_call(
|
||||
"switch",
|
||||
"turn_on",
|
||||
service_data={},
|
||||
target={"entity_id": "switch.pompe_chaudiere"},
|
||||
),
|
||||
]
|
||||
)
|
||||
assert mock_send_event.call_count >= 1
|
||||
mock_send_event.assert_has_calls(
|
||||
[
|
||||
call.send_vtherm_event(
|
||||
hass=hass,
|
||||
event_type=EventType.CENTRAL_BOILER_EVENT,
|
||||
entity=api.central_boiler_entity,
|
||||
data={"central_boiler": True},
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# 2. stop a climate
|
||||
open_degree_entity.set_native_value(0)
|
||||
mock_get_state_side_effect = SideEffects(
|
||||
{
|
||||
open_degree_entity.entity_id: State(
|
||||
open_degree_entity.entity_id,
|
||||
open_degree_entity.state,
|
||||
{"min": 0, "max": 100},
|
||||
),
|
||||
"number.mock_closing_degree": State(
|
||||
"number.mock_closing_degree", "0", {"min": 0, "max": 100}
|
||||
),
|
||||
"number.mock_offset_calibration": State(
|
||||
"number.mock_offset_calibration", "0", {"min": -12, "max": 12}
|
||||
),
|
||||
},
|
||||
State("unknown.entity_id", "unknown"),
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.core.ServiceRegistry.async_call"
|
||||
) as mock_service_call, patch(
|
||||
"custom_components.versatile_thermostat.binary_sensor.send_vtherm_event"
|
||||
) as mock_send_event, patch(
|
||||
"homeassistant.core.StateMachine.get",
|
||||
side_effect=mock_get_state_side_effect.get_side_effects(),
|
||||
):
|
||||
await send_temperature_change_event(entity, 25, now)
|
||||
climate1.set_hvac_mode(HVACMode.HEAT)
|
||||
climate1.set_hvac_action(HVACAction.IDLE)
|
||||
climate1.async_write_ha_state()
|
||||
open_degree_entity.set_native_value(0)
|
||||
# Wait for state event propagation
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
assert entity.hvac_action == HVACAction.OFF
|
||||
assert entity.device_actives == []
|
||||
|
||||
assert mock_service_call.call_count >= 1
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
"switch",
|
||||
"turn_off",
|
||||
service_data={},
|
||||
target={"entity_id": "switch.pompe_chaudiere"},
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
assert mock_send_event.call_count >= 1
|
||||
mock_send_event.assert_has_calls(
|
||||
[
|
||||
call.send_vtherm_event(
|
||||
hass=hass,
|
||||
event_type=EventType.CENTRAL_BOILER_EVENT,
|
||||
entity=api.central_boiler_entity,
|
||||
data={"central_boiler": False},
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
assert api.nb_active_device_for_boiler == 0
|
||||
assert boiler_binary_sensor.state == STATE_OFF
|
||||
|
||||
assert nb_device_active_sensor.state == 0
|
||||
assert nb_device_active_sensor.active_device_ids == []
|
||||
|
||||
entity.remove_thermostat()
|
||||
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ from .const import *
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
# @pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
# @pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
# this test fails if run in // with the next because the underlying_valve_regulation is mixed. Don't know why
|
||||
# @pytest.mark.skip
|
||||
async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get):
|
||||
@@ -138,13 +138,13 @@ async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get
|
||||
assert mock_service_call.call_count == 3
|
||||
mock_service_call.assert_has_calls(
|
||||
[
|
||||
call(domain='number', service='set_value', service_data={'value': 0}, target={'entity_id': 'number.mock_opening_degree'}),
|
||||
call(domain='number', service='set_value', service_data={'value': 100}, target={'entity_id': 'number.mock_closing_degree'}),
|
||||
call("climate","set_temperature",{
|
||||
"entity_id": "climate.mock_climate",
|
||||
"temperature": 15, # temp-min
|
||||
},
|
||||
),
|
||||
call(domain='number', service='set_value', service_data={'value': 0}, target={'entity_id': 'number.mock_opening_degree'}),
|
||||
call(domain='number', service='set_value', service_data={'value': 100}, target={'entity_id': 'number.mock_closing_degree'}),
|
||||
# we have no current_temperature yet
|
||||
# call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_offset_calibration'}),
|
||||
]
|
||||
@@ -300,7 +300,6 @@ async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_over_climate_valve_multi_presence(
|
||||
hass: HomeAssistant, skip_hass_states_get
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user