FIX testus
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "mikoz.black-py"
|
||||
"editor.defaultFormatter": "ms-python.python"
|
||||
},
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
162
tests/test_valve.py
Normal 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
|
||||
Reference in New Issue
Block a user