FIX testus

This commit is contained in:
Jean-Marc Collin
2023-10-22 19:52:32 +00:00
parent ae799adbd4
commit afe7c31f12
12 changed files with 266 additions and 73 deletions

View File

@@ -1,6 +1,6 @@
{
"[python]": {
"editor.defaultFormatter": "mikoz.black-py"
"editor.defaultFormatter": "ms-python.python"
},
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,

View File

@@ -189,7 +189,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._security_state = None
self._thermostat_type = None
self._is_over_climate = False
self._attr_translation_key = "versatile_thermostat"
@@ -262,7 +261,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self._underlyings = []
self._thermostat_type = entry_infos.get(CONF_THERMOSTAT_TYPE)
if self._thermostat_type == CONF_THERMOSTAT_CLIMATE:
self._is_over_climate = True
for climate in [
CONF_CLIMATE,
CONF_CLIMATE_2,
@@ -426,7 +424,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
# Initiate the ProportionalAlgorithm
if self._prop_algorithm is not None:
del self._prop_algorithm
if not self._is_over_climate:
if not self.is_over_climate:
self._prop_algorithm = PropAlgorithm(
self._proportional_function,
self._tpi_coef_int,
@@ -711,7 +709,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.hass.create_task(self._check_switch_initial_state())
# Start the control_heating
# starts a cycle if we are in over_climate type
if self._is_over_climate:
if self.is_over_climate:
self.async_on_remove(
async_track_time_interval(
self.hass,
@@ -809,6 +807,21 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
def __str__(self):
return f"VersatileThermostat-{self.name}"
@property
def is_over_climate(self):
""" True if the Thermostat is over_climate"""
return False
@property
def is_over_switch(self):
""" True if the Thermostat is over_switch"""
return False
@property
def is_over_valve(self):
""" True if the Thermostat is over_valve"""
return False
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
@@ -835,7 +848,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
@property
def hvac_modes(self):
"""List of available operation modes."""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).hvac_modes
return self._hvac_list
@@ -851,7 +864,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.FAN_MODE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).fan_mode
return None
@@ -862,7 +875,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.FAN_MODE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).fan_modes
return []
@@ -873,7 +886,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.SWING_MODE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).swing_mode
return None
@@ -884,7 +897,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.SWING_MODE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).swing_modes
return None
@@ -892,7 +905,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement."""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).temperature_unit
return self._unit
@@ -902,7 +915,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"""Return current operation."""
# Issue #114 - returns my current hvac_mode and not the underlying hvac_mode which could be different
# delta will be managed by climate_state_change event.
# if self._is_over_climate:
# if self.is_over_climate:
# if one not OFF -> return it
# else OFF
# for under in self._underlyings:
@@ -918,7 +931,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Need to be one of CURRENT_HVAC_*.
"""
if self._is_over_climate:
if self.is_over_climate:
# if one not IDLE or OFF -> return it
# else if one IDLE -> IDLE
# else OFF
@@ -951,7 +964,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
@property
def supported_features(self):
"""Return the list of supported features."""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).supported_features | self._support_flags
return self._support_flags
@@ -972,7 +985,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
@property
def target_temperature_step(self) -> float | None:
"""Return the supported step of target temperature."""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).target_temperature_step
return None
@@ -983,7 +996,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.TARGET_TEMPERATURE_RANGE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).target_temperature_high
return None
@@ -994,7 +1007,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.TARGET_TEMPERATURE_RANGE.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).target_temperature_low
return None
@@ -1005,7 +1018,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
Requires ClimateEntityFeature.AUX_HEAT.
"""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).is_aux_heat
return None
@@ -1013,7 +1026,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
@property
def mean_cycle_power(self) -> float | None:
"""Returns the mean power consumption during the cycle"""
if not self._device_power or self._is_over_climate:
if not self._device_power or self.is_over_climate:
return None
return float(
@@ -1093,12 +1106,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"""
return self._attr_preset_modes
@property
def is_over_climate(self) -> bool | None:
"""return True is the thermostat is over a climate
or False is over switch"""
return self._is_over_climate
@property
def last_temperature_slope(self) -> float | None:
"""Return the last temperature slope curve if any"""
@@ -1133,14 +1140,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
def turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
if self._is_over_climate and self.underlying_entity(0):
if self.is_over_climate and self.underlying_entity(0):
return self.underlying_entity(0).turn_aux_heat_on()
raise NotImplementedError()
async def async_turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
if self._is_over_climate:
if self.is_over_climate:
for under in self._underlyings:
await under.async_turn_aux_heat_on()
@@ -1148,7 +1155,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
def turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
if self._is_over_climate:
if self.is_over_climate:
for under in self._underlyings:
return under.turn_aux_heat_off()
@@ -1156,7 +1163,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
async def async_turn_aux_heat_off(self) -> None:
"""Turn auxiliary heater off."""
if self._is_over_climate:
if self.is_over_climate:
for under in self._underlyings:
await under.async_turn_aux_heat_off()
@@ -1273,7 +1280,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
else:
# Select _ac presets if in COOL Mode (or over_switch with _ac_mode)
if self._ac_mode and (
self._hvac_mode == HVACMode.COOL or not self._is_over_climate
self._hvac_mode == HVACMode.COOL or not self.is_over_climate
):
preset_mode = preset_mode + PRESET_AC_SUFFIX
@@ -1292,7 +1299,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
async def async_set_fan_mode(self, fan_mode):
"""Set new target fan mode."""
_LOGGER.info("%s - Set fan mode: %s", self, fan_mode)
if fan_mode is None or not self._is_over_climate:
if fan_mode is None or not self.is_over_climate:
return
for under in self._underlyings:
@@ -1303,7 +1310,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
async def async_set_humidity(self, humidity: int):
"""Set new target humidity."""
_LOGGER.info("%s - Set fan mode: %s", self, humidity)
if humidity is None or not self._is_over_climate:
if humidity is None or not self.is_over_climate:
return
for under in self._underlyings:
await under.set_humidity(humidity)
@@ -1313,7 +1320,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
async def async_set_swing_mode(self, swing_mode):
"""Set new target swing operation."""
_LOGGER.info("%s - Set fan mode: %s", self, swing_mode)
if swing_mode is None or not self._is_over_climate:
if swing_mode is None or not self.is_over_climate:
return
for under in self._underlyings:
await under.set_swing_mode(swing_mode)
@@ -1335,7 +1342,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
async def _async_internal_set_temperature(self, temperature):
"""Set the target temperature and the target temperature of underlying climate if any"""
self._target_temp = temperature
if not self._is_over_climate:
if not self.is_over_climate:
return
for under in self._underlyings:
@@ -1737,7 +1744,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
changes = True
self._hvac_mode = new_hvac_mode
# Update all underlyings state
if self._is_over_climate:
if self.is_over_climate:
for under in self._underlyings:
await under.set_hvac_mode(new_hvac_mode)
@@ -1749,7 +1756,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
new_state.attributes,
)
if (
self._is_over_climate
self.is_over_climate
and new_state.attributes
and (new_target_temp := new_state.attributes.get("temperature"))
and new_target_temp != self.target_temperature
@@ -2094,7 +2101,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
if (
old_hvac_mode == HVACMode.OFF
and self.hvac_mode != HVACMode.OFF
and self._is_over_climate
and self.is_over_climate
):
_LOGGER.info(
"%s - force resent target temp cause we turn on some over climate"
@@ -2136,7 +2143,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"%s - overpowering is detected. Heater preset will be set to 'power'",
self,
)
if self._is_over_climate:
if self.is_over_climate:
self.save_hvac_mode()
self.save_preset_mode()
await self._async_underlying_entity_turn_off()
@@ -2162,7 +2169,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self,
self._saved_preset_mode,
)
if self._is_over_climate:
if self.is_over_climate:
await self.restore_hvac_mode(False)
await self.restore_preset_mode()
self.send_event(
@@ -2194,12 +2201,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
delta_temp > self._security_delay_min
or delta_ext_temp > self._security_delay_min
)
climate_cond: bool = self._is_over_climate and self.hvac_action not in [
climate_cond: bool = self.is_over_climate and self.hvac_action not in [
HVACAction.COOLING,
HVACAction.IDLE,
]
switch_cond: bool = (
not self._is_over_climate
not self.is_over_climate
and self._prop_algorithm is not None
and self._prop_algorithm.calculated_on_percent
>= self._security_min_on_percent
@@ -2274,7 +2281,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
self.save_preset_mode()
await self._async_set_preset_mode_internal(PRESET_SECURITY)
# Turn off the underlying climate or heater if security default on_percent is 0
if self._is_over_climate or self._security_default_on_percent <= 0.0:
if self.is_over_climate or self._security_default_on_percent <= 0.0:
await self.async_set_hvac_mode(HVACMode.OFF, False)
if self._prop_algorithm:
self._prop_algorithm.set_security(self._security_default_on_percent)
@@ -2304,7 +2311,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
)
self._security_state = False
# Restore hvac_mode if previously saved
if self._is_over_climate or self._security_default_on_percent <= 0.0:
if self.is_over_climate or self._security_default_on_percent <= 0.0:
await self.restore_hvac_mode(False)
await self.restore_preset_mode()
if self._prop_algorithm:
@@ -2360,7 +2367,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
return True
security: bool = await self.check_security()
if security and self._is_over_climate:
if security and self.is_over_climate:
_LOGGER.debug("%s - End of cycle (security and over climate)", self)
return True
@@ -2372,7 +2379,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
await self._async_underlying_entity_turn_off()
return True
if not self._is_over_climate:
if not self.is_over_climate:
for under in self._underlyings:
await under.start_cycle(
self._hvac_mode,
@@ -2389,7 +2396,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
update the custom attributes and write the state
"""
_LOGGER.debug("%s - recalculate all", self)
if not self._is_over_climate:
if not self.is_over_climate:
self._prop_algorithm.calculate(
self._target_temp,
self._cur_temp,
@@ -2405,10 +2412,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
return
added_energy = 0
if self._is_over_climate and self._underlying_climate_delta_t is not None:
if self.is_over_climate and self._underlying_climate_delta_t is not None:
added_energy = self._device_power * self._underlying_climate_delta_t
if not self._is_over_climate and self.mean_cycle_power is not None:
if not self.is_over_climate and self.mean_cycle_power is not None:
added_energy = self.mean_cycle_power * float(self._cycle_min) / 60.0
self._total_energy += added_energy
@@ -2481,7 +2488,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
"power_sensor_entity_id": self._power_sensor_entity_id,
"max_power_sensor_entity_id": self._max_power_sensor_entity_id,
}
if self._is_over_climate:
if self.is_over_climate:
self._attr_extra_state_attributes[
"underlying_climate_0"
] = self._underlyings[0].entity_id

View File

@@ -9,3 +9,8 @@ class ThermostatOverClimate(BaseThermostat):
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
"""Initialize the thermostat over switch."""
super().__init__(hass, unique_id, name, entry_infos)
@property
def is_over_climate(self):
""" True if the Thermostat is over_climate"""
return True

View File

@@ -11,3 +11,8 @@ class ThermostatOverSwitch(BaseThermostat):
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
"""Initialize the thermostat over switch."""
super().__init__(hass, unique_id, name, entry_infos)
@property
def is_over_switch(self):
""" True if the Thermostat is over_switch"""
return True

View File

@@ -9,3 +9,8 @@ class ThermostatOverValve(BaseThermostat):
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
"""Initialize the thermostat over switch."""
super().__init__(hass, unique_id, name, entry_infos)
@property
def is_over_valve(self):
""" True if the Thermostat is over_valve"""
return True

View File

@@ -1,3 +1,5 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Some common resources """
import asyncio
import logging
@@ -550,7 +552,7 @@ async def send_climate_change_event_with_temperature(
def cancel_switchs_cycles(entity: BaseThermostat):
"""This method will cancel all running cycle on all underlying switch entity"""
if entity._is_over_climate:
if entity.is_over_climate:
return
for under in entity._underlyings:
under._cancel_cycle()

View File

@@ -1,10 +1,12 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Test the Window management """
from unittest.mock import patch, call
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
from datetime import datetime, timedelta
import logging
from .commons import *
logging.getLogger().setLevel(logging.DEBUG)
@@ -49,7 +51,7 @@ async def test_bug_56(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity
@@ -126,7 +128,7 @@ async def test_bug_63(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
@@ -178,7 +180,7 @@ async def test_bug_64(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
@@ -230,7 +232,7 @@ async def test_bug_66(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
@@ -387,7 +389,7 @@ async def test_bug_82(
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity._is_over_climate is True
assert entity.is_over_climate is True
# assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
# assert entity.hvac_mode is None
@@ -434,7 +436,7 @@ async def test_bug_82(
"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:
):
event_timestamp = now - timedelta(minutes=6)
# set temperature to 15 so that on_percent will be > security_min_on_percent (0.2)
@@ -491,7 +493,7 @@ async def test_bug_101(
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity._is_over_climate is True
assert entity.is_over_climate is True
assert entity.hvac_mode is HVACMode.OFF
# because the underlying is heating. In real life the underlying should be shut-off
assert entity.hvac_action is HVACAction.HEATING
@@ -540,6 +542,3 @@ async def test_bug_101(
await send_climate_change_event_with_temperature(entity, HVACMode.HEAT, HVACMode.HEAT, HVACAction.OFF, HVACAction.OFF, event_timestamp, 12.75)
assert entity.target_temperature == 12.75
assert entity.preset_mode is PRESET_NONE

View File

@@ -235,7 +235,7 @@ async def test_security_over_climate(
assert entity
assert entity.name == "TheOverClimateMockName"
assert entity._is_over_climate is True
assert entity.is_over_climate is True
# Because the underlying is HEATING. In real life the underlying will be shut-off
assert entity.hvac_action is HVACAction.HEATING

View File

@@ -1,3 +1,5 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Test the normal start of a Thermostat """
from datetime import timedelta, datetime
@@ -66,7 +68,7 @@ async def test_sensors_over_switch(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
@@ -229,7 +231,7 @@ async def test_sensors_over_climate(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity
@@ -361,7 +363,7 @@ async def test_sensors_over_climate_minimal(
},
)
entity: VersatileThermostat = await create_thermostat(
entity: BaseThermostat = await create_thermostat(
hass, entry, "climate.theoverclimatemockname"
)
assert entity

View File

@@ -1,3 +1,5 @@
# pylint: disable=wildcard-import, unused-wildcard-import, protected-access, unused-argument, line-too-long
""" Test the normal start of a Thermostat """
from unittest.mock import patch, call
@@ -11,6 +13,8 @@ from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DO
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_climate import ThermostatOverClimate
from custom_components.versatile_thermostat.thermostat_switch import ThermostatOverSwitch
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -41,12 +45,13 @@ async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_s
if entity.entity_id == entity_id:
return entity
entity: VersatileThermostat = find_my_entity("climate.theoverswitchmockname")
entity: BaseThermostat = find_my_entity("climate.theoverswitchmockname")
assert entity
assert isinstance(entity, ThermostatOverSwitch)
assert entity.name == "TheOverSwitchMockName"
assert entity._is_over_climate is False
assert entity.is_over_climate is False
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
assert entity.target_temperature == entity.min_temp
@@ -112,9 +117,10 @@ async def test_over_climate_full_start(hass: HomeAssistant, skip_hass_states_is_
entity = find_my_entity("climate.theoverclimatemockname")
assert entity
assert isinstance(entity, ThermostatOverClimate)
assert entity.name == "TheOverClimateMockName"
assert entity._is_over_climate is True
assert entity.is_over_climate is True
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
assert entity.target_temperature == entity.min_temp
@@ -173,12 +179,12 @@ async def test_over_4switch_full_start(hass: HomeAssistant, skip_hass_states_is_
if entity.entity_id == entity_id:
return entity
entity: VersatileThermostat = find_my_entity("climate.theover4switchmockname")
entity: BaseThermostat = find_my_entity("climate.theover4switchmockname")
assert entity
assert entity.name == "TheOver4SwitchMockName"
assert entity._is_over_climate is False
assert entity.is_over_climate is False
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
assert entity.target_temperature == entity.min_temp

View File

@@ -12,6 +12,7 @@ from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DO
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_switch import ThermostatOverSwitch
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@@ -48,9 +49,10 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
entity: BaseThermostat = find_my_entity("climate.theoverswitchmockname")
assert entity
assert isinstance(entity, ThermostatOverSwitch)
assert entity.name == "TheOverSwitchMockName"
assert entity._is_over_climate is False # pylint: disable=protected-access
assert entity.is_over_climate is False # pylint: disable=protected-access
assert entity.ac_mode is True
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
@@ -136,5 +138,3 @@ async def test_over_switch_ac_full_start(hass: HomeAssistant, skip_hass_states_i
assert entity.hvac_mode is HVACMode.COOL
assert (entity.hvac_action is HVACAction.OFF or entity.hvac_action is HVACAction.IDLE)
assert entity.target_temperature == 27 # eco_ac_away

162
tests/test_valve.py Normal file
View File

@@ -0,0 +1,162 @@
""" Test the normal start of a Switch AC Thermostat """
from unittest.mock import patch, call
from datetime import datetime, timedelta
from homeassistant.core import HomeAssistant
from homeassistant.components.climate import HVACAction, HVACMode
from homeassistant.config_entries import ConfigEntryState
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
from custom_components.versatile_thermostat.thermostat_valve import ThermostatOverValve
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_over_valve_full_start(hass: HomeAssistant, skip_hass_states_is_state): # pylint: disable=unused-argument
"""Test the normal full start of a thermostat in thermostat_over_switch type"""
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverValveMockName",
unique_id="uniqueId",
data={
CONF_NAME: "TheOverValveMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_VALVE,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_VALVE: "number.mock_valve",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
# CONF_DEVICE_POWER: 100,
},
)
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
with patch(
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
) as mock_send_event:
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED
def find_my_entity(entity_id) -> ClimateEntity:
"""Find my new entity"""
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
for entity in component.entities:
if entity.entity_id == entity_id:
return entity
# The name is in the CONF and not the title of the entry
entity: BaseThermostat = find_my_entity("climate.theovervalvemockname")
assert entity
assert isinstance(entity, ThermostatOverValve)
assert entity.name == "TheOverValveMockName"
assert entity.is_over_climate is False
assert entity.is_over_switch is False
assert entity.is_over_valve is True
assert entity.ac_mode is False
assert entity.hvac_action is HVACAction.OFF
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_modes == [HVACMode.COOL, HVACMode.OFF]
assert entity.target_temperature == entity.max_temp
assert entity.preset_modes == [
PRESET_NONE,
PRESET_ECO,
PRESET_COMFORT,
PRESET_BOOST,
PRESET_ACTIVITY,
]
assert entity.preset_mode is PRESET_NONE
assert entity._security_state is False # pylint: disable=protected-access
assert entity._window_state is None # pylint: disable=protected-access
assert entity._motion_state is None # pylint: disable=protected-access
assert entity._presence_state is None # pylint: disable=protected-access
assert entity._prop_algorithm is not None # pylint: disable=protected-access
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
assert mock_send_event.call_count == 2
mock_send_event.assert_has_calls(
[
call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_NONE}),
call.send_event(
EventType.HVAC_MODE_EVENT,
{"hvac_mode": HVACMode.OFF},
),
]
)
# Select a hvacmode, presence and preset
await entity.async_set_hvac_mode(HVACMode.COOL)
assert entity.hvac_mode is HVACMode.COOL
event_timestamp = now - timedelta(minutes=4)
await send_presence_change_event(entity, True, False, event_timestamp)
assert entity._presence_state == STATE_ON # pylint: disable=protected-access
await entity.async_set_hvac_mode(HVACMode.COOL)
assert entity.hvac_mode is HVACMode.COOL
await entity.async_set_preset_mode(PRESET_COMFORT)
assert entity.preset_mode is PRESET_COMFORT
assert entity.target_temperature == 23
# switch to Eco
await entity.async_set_preset_mode(PRESET_ECO)
assert entity.preset_mode is PRESET_ECO
assert entity.target_temperature == 25
# Unset the presence
event_timestamp = now - timedelta(minutes=3)
await send_presence_change_event(entity, False, True, event_timestamp)
assert entity._presence_state == STATE_OFF # pylint: disable=protected-access
assert entity.target_temperature == 27 # eco_ac_away
# Open a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = now - timedelta(minutes=2)
try_condition = await send_window_change_event(entity, True, False, event_timestamp)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.OFF
assert entity.hvac_action is HVACAction.OFF
assert entity.target_temperature == 27 # eco_ac_away
# Close a window
with patch(
"homeassistant.helpers.condition.state", return_value=True
):
event_timestamp = now - timedelta(minutes=2)
try_condition = await send_window_change_event(entity, False, True, event_timestamp)
# Confirme the window event
await try_condition(None)
assert entity.hvac_mode is HVACMode.COOL
assert (entity.hvac_action is HVACAction.OFF or entity.hvac_action is HVACAction.IDLE)
assert entity.target_temperature == 27 # eco_ac_away