From 6c91c197a1cae541bc61d4126b05d8c534d70e5c Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Tue, 31 Dec 2024 15:42:33 +0000 Subject: [PATCH] Add safety feature_safety_manager Rename config attribute from security_ to safety_ --- .../versatile_thermostat/__init__.py | 17 + .../versatile_thermostat/base_thermostat.py | 255 +++----------- .../versatile_thermostat/binary_sensor.py | 2 +- .../versatile_thermostat/climate.py | 4 +- .../versatile_thermostat/config_flow.py | 2 +- .../versatile_thermostat/config_schema.py | 6 +- .../versatile_thermostat/const.py | 20 +- .../feature_auto_start_stop_manager.py | 2 +- .../feature_safety_manager.py | 310 ++++++++++++++++++ .../versatile_thermostat/prop_algorithm.py | 4 +- .../versatile_thermostat/services.yaml | 2 +- .../versatile_thermostat/underlyings.py | 2 +- documentation/en/one-page.md | 2 +- documentation/en/reference.md | 2 +- documentation/fr/one-page.md | 2 +- documentation/fr/reference.md | 2 +- tests/commons.py | 12 +- tests/const.py | 13 +- tests/test_auto_fan_mode.py | 12 +- tests/test_auto_start_stop.py | 32 +- tests/test_binary_sensors.py | 28 +- tests/test_bugs.py | 24 +- tests/test_central_boiler.py | 36 +- tests/test_central_config.py | 50 +-- tests/test_central_mode.py | 54 +-- tests/test_config_flow.py | 48 +-- tests/test_inverted_switch.py | 4 +- tests/test_last_seen.py | 32 +- tests/test_motion.py | 20 +- tests/test_multiple_switch.py | 24 +- tests/test_overclimate.py | 20 +- tests/test_overclimate_valve.py | 2 +- tests/test_power.py | 16 +- tests/{test_security.py => test_safety.py} | 136 ++++++-- tests/test_sensors.py | 12 +- tests/test_start.py | 8 +- tests/test_switch_ac.py | 2 +- tests/test_switch_keep_alive.py | 4 +- tests/test_temp_number.py | 24 +- tests/test_tpi.py | 16 +- tests/test_valve.py | 14 +- tests/test_window.py | 66 ++-- 42 files changed, 795 insertions(+), 548 deletions(-) create mode 100644 custom_components/versatile_thermostat/feature_safety_manager.py rename tests/{test_security.py => test_safety.py} (83%) diff --git a/custom_components/versatile_thermostat/__init__.py b/custom_components/versatile_thermostat/__init__.py index 4dbe3e6..e0841ea 100644 --- a/custom_components/versatile_thermostat/__init__.py +++ b/custom_components/versatile_thermostat/__init__.py @@ -29,6 +29,9 @@ from .const import ( CONF_AUTO_REGULATION_EXPERT, CONF_SHORT_EMA_PARAMS, CONF_SAFETY_MODE, + CONF_SAFETY_DELAY_MIN, + CONF_SAFETY_MIN_ON_PERCENT, + CONF_SAFETY_DEFAULT_ON_PERCENT, CONF_THERMOSTAT_CENTRAL_CONFIG, CONF_THERMOSTAT_TYPE, CONF_USE_WINDOW_FEATURE, @@ -291,6 +294,20 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry): ]: new.pop(key, None) + # Migration 2.0 to 2.1 -> rename security parameters into safety + + if config_entry.version == CONFIG_VERSION and config_entry.minor_version == 0: + for key in [ + "security_delay_min", + "security_min_on_percent", + "security_default_on_percent", + ]: + new_key = key.replace("security_", "safety_") + old_value = config_entry.data.get(key, None) + if old_value is not None: + new[new_key] = old_value + new.pop(key, None) + hass.config_entries.async_update_entry( config_entry, data=new, diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index 2e25ec4..ae88178 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -70,6 +70,7 @@ from .feature_presence_manager import FeaturePresenceManager from .feature_power_manager import FeaturePowerManager from .feature_motion_manager import FeatureMotionManager from .feature_window_manager import FeatureWindowManager +from .feature_safety_manager import FeatureSafetyManager _LOGGER = logging.getLogger(__name__) @@ -102,9 +103,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): "saved_preset_mode", "saved_target_temp", "saved_hvac_mode", - "security_delay_min", - "security_min_on_percent", - "security_default_on_percent", "last_temperature_datetime", "last_ext_temperature_datetime", "minimal_activation_delay_sec", @@ -170,11 +168,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._cur_ext_temp = None self._should_relaunch_control_heating = None - self._security_delay_min = None - self._security_min_on_percent = None - self._security_default_on_percent = None - self._security_state = None - self._thermostat_type = None self._attr_translation_key = "versatile_thermostat" @@ -224,11 +217,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._power_manager: FeaturePowerManager = FeaturePowerManager(self, hass) self._motion_manager: FeatureMotionManager = FeatureMotionManager(self, hass) self._window_manager: FeatureWindowManager = FeatureWindowManager(self, hass) + self._safety_manager: FeatureSafetyManager = FeatureSafetyManager(self, hass) self.register_manager(self._presence_manager) self.register_manager(self._power_manager) self.register_manager(self._motion_manager) self.register_manager(self._window_manager) + self.register_manager(self._safety_manager) self.post_init(entry_infos) @@ -373,21 +368,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) self._tpi_coef_ext = 0 - self._security_delay_min = entry_infos.get(CONF_SECURITY_DELAY_MIN) - self._security_min_on_percent = ( - entry_infos.get(CONF_SECURITY_MIN_ON_PERCENT) - if entry_infos.get(CONF_SECURITY_MIN_ON_PERCENT) is not None - else DEFAULT_SECURITY_MIN_ON_PERCENT - ) - self._security_default_on_percent = ( - entry_infos.get(CONF_SECURITY_DEFAULT_ON_PERCENT) - if entry_infos.get(CONF_SECURITY_DEFAULT_ON_PERCENT) is not None - else DEFAULT_SECURITY_DEFAULT_ON_PERCENT - ) self._minimal_activation_delay = entry_infos.get(CONF_MINIMAL_ACTIVATION_DELAY) self._last_temperature_measure = self.now self._last_ext_temperature_measure = self.now - self._security_state = False # Initiate the ProportionalAlgorithm if self._prop_algorithm is not None: @@ -619,6 +602,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): "saved_preset_mode", None ) + self._hvac_off_reason = old_state.attributes.get("hvac_mode_reason", None) + old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY) self._total_energy = old_total_energy if old_total_energy is not None else 0 _LOGGER.debug( @@ -657,7 +642,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self.send_event(EventType.HVAC_MODE_EVENT, {"hvac_mode": self._hvac_mode}) _LOGGER.info( - "%s - restored state is target_temp=%.1f, preset_mode=%s, hvac_mode=%s", + "%s - restored state is target_temp=%s, preset_mode=%s, hvac_mode=%s", self, self._target_temp, self._attr_preset_mode, @@ -820,6 +805,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Return the sensor temperature.""" return self._cur_temp + @property + def current_outdoor_temperature(self) -> float | None: + """Return the outdoor sensor temperature.""" + return self._cur_ext_temp + @property def is_aux_heat(self) -> bool | None: """Return true if aux heater. @@ -861,6 +851,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Get the window manager""" return self._window_manager + @property + def safety_manager(self) -> FeatureSafetyManager | None: + """Get the safety manager""" + return self._safety_manager + @property def window_state(self) -> str | None: """Get the window_state""" @@ -877,9 +872,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): return self._window_manager.is_window_bypass @property - def security_state(self) -> bool | None: - """Get the security_state""" - return self._security_state + def safety_state(self) -> str | None: + """Get the safety_state""" + return self._safety_manager.safety_state @property def motion_state(self) -> str | None: @@ -1112,8 +1107,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): # I don't think we need to call async_write_ha_state if we didn't change the state return - # In safety mode don't change preset but memorise the new expected preset when security will be off - if preset_mode != PRESET_SECURITY and self._security_state: + # In safety mode don't change preset but memorise the new expected preset when safety will be off + if preset_mode != PRESET_SAFETY and self._safety_manager.is_safety_detected: _LOGGER.debug( "%s - is in safety mode. Just memorise the new expected ", self ) @@ -1182,10 +1177,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): else self._attr_min_temp ) - if preset_mode == PRESET_SECURITY: + if preset_mode == PRESET_SAFETY: return ( self._target_temp - ) # in security just keep the current target temperature, the thermostat should be off + ) # in safety just keep the current target temperature, the thermostat should be off if preset_mode == PRESET_POWER: return self._power_manager.power_temperature if preset_mode == PRESET_ACTIVITY: @@ -1333,8 +1328,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) # try to restart if we were in safety mode - if self._security_state: - await self.check_safety() + if self._safety_manager.is_safety_detected: + await self._safety_manager.refresh_state() except ValueError as err: # La conversion a échoué, la chaîne n'est pas au format ISO 8601 @@ -1399,8 +1394,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) # try to restart if we were in safety mode - if self._security_state: - await self.check_safety() + if self._safety_manager.is_safety_detected: + await self._safety_manager.refresh_state() # check window_auto return await self._window_manager.manage_window_auto() @@ -1426,8 +1421,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) # try to restart if we were in safety mode - if self._security_state: - await self.check_safety() + if self._safety_manager.is_safety_detected: + await self._safety_manager.refresh_state() except ValueError as ex: _LOGGER.error("Unable to update external temperature from sensor: %s", ex) @@ -1572,163 +1567,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Get now. The local datetime or the overloaded _set_now date""" return self._now if self._now is not None else NowClass.get_now(self._hass) - async def check_safety(self) -> bool: - """Check if last temperature date is too long""" - - now = self.now - delta_temp = ( - now - self._last_temperature_measure.replace(tzinfo=self._current_tz) - ).total_seconds() / 60.0 - delta_ext_temp = ( - now - self._last_ext_temperature_measure.replace(tzinfo=self._current_tz) - ).total_seconds() / 60.0 - - mode_cond = self._hvac_mode != HVACMode.OFF - - api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api() - is_outdoor_checked = ( - not api.safety_mode - or api.safety_mode.get("check_outdoor_sensor") is not False - ) - - temp_cond: bool = delta_temp > self._security_delay_min or ( - is_outdoor_checked and delta_ext_temp > self._security_delay_min - ) - 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 - and self._prop_algorithm is not None - and self._prop_algorithm.calculated_on_percent - >= self._security_min_on_percent - ) - - _LOGGER.debug( - "%s - checking security delta_temp=%.1f delta_ext_temp=%.1f mod_cond=%s temp_cond=%s climate_cond=%s switch_cond=%s", - self, - delta_temp, - delta_ext_temp, - mode_cond, - temp_cond, - climate_cond, - switch_cond, - ) - - # Issue 99 - a climate is regulated by the device itself and not by VTherm. So a VTherm should never be in security ! - shouldClimateBeInSecurity = False # temp_cond and climate_cond - shouldSwitchBeInSecurity = temp_cond and switch_cond - shouldBeInSecurity = shouldClimateBeInSecurity or shouldSwitchBeInSecurity - - shouldStartSecurity = ( - mode_cond and not self._security_state and shouldBeInSecurity - ) - # attr_preset_mode is not necessary normaly. It is just here to be sure - shouldStopSecurity = ( - self._security_state - and not shouldBeInSecurity - and self._attr_preset_mode == PRESET_SECURITY - ) - - # Logging and event - if shouldStartSecurity: - if shouldClimateBeInSecurity: - _LOGGER.warning( - "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Setting it into safety mode", - self, - self._security_delay_min, - delta_temp, - delta_ext_temp, - self.hvac_action, - ) - elif shouldSwitchBeInSecurity: - _LOGGER.warning( - "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f %%) is over defined value (%.2f %%). Set it into safety mode", - self, - self._security_delay_min, - delta_temp, - delta_ext_temp, - self._prop_algorithm.on_percent * 100, - self._security_min_on_percent * 100, - ) - - self.send_event( - EventType.TEMPERATURE_EVENT, - { - "last_temperature_measure": self._last_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "last_ext_temperature_measure": self._last_ext_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "current_temp": self._cur_temp, - "current_ext_temp": self._cur_ext_temp, - "target_temp": self.target_temperature, - }, - ) - - # Start safety mode - if shouldStartSecurity: - self._security_state = True - self.save_hvac_mode() - self.save_preset_mode() - if self._prop_algorithm: - self._prop_algorithm.set_security(self._security_default_on_percent) - 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: - await self.async_set_hvac_mode(HVACMode.OFF, False) - - self.send_event( - EventType.SECURITY_EVENT, - { - "type": "start", - "last_temperature_measure": self._last_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "last_ext_temperature_measure": self._last_ext_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "current_temp": self._cur_temp, - "current_ext_temp": self._cur_ext_temp, - "target_temp": self.target_temperature, - }, - ) - - # Stop safety mode - if shouldStopSecurity: - _LOGGER.warning( - "%s - End of safety mode. restoring hvac_mode to %s and preset_mode to %s", - self, - self._saved_hvac_mode, - self._saved_preset_mode, - ) - self._security_state = False - if self._prop_algorithm: - self._prop_algorithm.unset_security() - # Restore hvac_mode if previously saved - if self.is_over_climate or self._security_default_on_percent <= 0.0: - await self.restore_hvac_mode(False) - await self.restore_preset_mode() - self.send_event( - EventType.SECURITY_EVENT, - { - "type": "end", - "last_temperature_measure": self._last_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "last_ext_temperature_measure": self._last_ext_temperature_measure.replace( - tzinfo=self._current_tz - ).isoformat(), - "current_temp": self._cur_temp, - "current_ext_temp": self._cur_ext_temp, - "target_temp": self.target_temperature, - }, - ) - - return shouldBeInSecurity - @property def is_initialized(self) -> bool: """Check if all underlyings are initialized @@ -1740,10 +1578,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """The main function used to run the calculation at each cycle""" _LOGGER.debug( - "%s - Checking new cycle. hvac_mode=%s, security_state=%s, preset_mode=%s", + "%s - Checking new cycle. hvac_mode=%s, safety_state=%s, preset_mode=%s", self, self._hvac_mode, - self._security_state, + self._safety_manager.safety_state, self._attr_preset_mode, ) @@ -1763,9 +1601,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.debug("%s - End of cycle (overpowering)", self) return True - security: bool = await self.check_safety() - if security and self.is_over_climate: - _LOGGER.debug("%s - End of cycle (security and over climate)", self) + safety: bool = await self._safety_manager.refresh_state() + if safety and self.is_over_climate: + _LOGGER.debug("%s - End of cycle (safety and over climate)", self) return True # Stop here if we are off @@ -1834,16 +1672,12 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): "saved_preset_mode": self._saved_preset_mode, "saved_target_temp": self._saved_target_temp, "saved_hvac_mode": self._saved_hvac_mode, - "security_delay_min": self._security_delay_min, - "security_min_on_percent": self._security_min_on_percent, - "security_default_on_percent": self._security_default_on_percent, "last_temperature_datetime": self._last_temperature_measure.astimezone( self._current_tz ).isoformat(), "last_ext_temperature_datetime": self._last_ext_temperature_measure.astimezone( self._current_tz ).isoformat(), - "security_state": self._security_state, "minimal_activation_delay_sec": self._minimal_activation_delay, ATTR_TOTAL_ENERGY: self.total_energy, "last_update_datetime": self.now.isoformat(), @@ -1956,14 +1790,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) await self.async_control_heating(force=True) - async def service_set_security( + async def SERVICE_SET_SAFETY( self, delay_min: int | None, min_on_percent: float | None, default_on_percent: float | None, ): """Called by a service call: - service: versatile_thermostat.set_security + service: versatile_thermostat.set_safety data: delay_min: 15 min_on_percent: 0.5 @@ -1972,21 +1806,23 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): entity_id: climate.thermostat_2 """ _LOGGER.info( - "%s - Calling service_set_security, delay_min: %s, min_on_percent: %s %%, default_on_percent: %s %%", + "%s - Calling SERVICE_SET_SAFETY, delay_min: %s, min_on_percent: %s %%, default_on_percent: %s %%", self, delay_min, min_on_percent * 100, default_on_percent * 100, ) if delay_min: - self._security_delay_min = delay_min + self._safety_manager.set_safety_delay_min(delay_min) if min_on_percent: - self._security_min_on_percent = min_on_percent + self._safety_manager.set_safety_min_on_percent(min_on_percent) if default_on_percent: - self._security_default_on_percent = default_on_percent + self._safety_manager.set_safety_default_on_percent(default_on_percent) - if self._prop_algorithm and self._security_state: - self._prop_algorithm.set_security(self._security_default_on_percent) + if self._prop_algorithm: + self._prop_algorithm.set_safety( + self._safety_manager.safety_default_on_percent + ) await self.async_control_heating() self.update_custom_attributes() @@ -2070,7 +1906,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): self._support_flags = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE for key, _ in CONF_PRESETS.items(): - if self.find_preset_temp(key) > 0: + preset_value = self.find_preset_temp(key) + if preset_value is not None and preset_value > 0: self._attr_preset_modes.append(key) _LOGGER.debug( diff --git a/custom_components/versatile_thermostat/binary_sensor.py b/custom_components/versatile_thermostat/binary_sensor.py index 7f7d446..c9b4bc7 100644 --- a/custom_components/versatile_thermostat/binary_sensor.py +++ b/custom_components/versatile_thermostat/binary_sensor.py @@ -109,7 +109,7 @@ class SecurityBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity): # _LOGGER.debug("%s - climate state change", self._attr_unique_id) old_state = self._attr_is_on - self._attr_is_on = self.my_climate.security_state is True + self._attr_is_on = self.my_climate.safety_manager.is_safety_detected if old_state != self._attr_is_on: self.async_write_ha_state() return diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py index 4fc586e..61334d8 100644 --- a/custom_components/versatile_thermostat/climate.py +++ b/custom_components/versatile_thermostat/climate.py @@ -97,13 +97,13 @@ async def async_setup_entry( ) platform.async_register_entity_service( - SERVICE_SET_SECURITY, + SERVICE_SET_SAFETY, { vol.Optional("delay_min"): cv.positive_int, vol.Optional("min_on_percent"): vol.Coerce(float), vol.Optional("default_on_percent"): vol.Coerce(float), }, - "service_set_security", + "SERVICE_SET_SAFETY", ) platform.async_register_entity_service( diff --git a/custom_components/versatile_thermostat/config_flow.py b/custom_components/versatile_thermostat/config_flow.py index 7e824a9..9190647 100644 --- a/custom_components/versatile_thermostat/config_flow.py +++ b/custom_components/versatile_thermostat/config_flow.py @@ -6,7 +6,7 @@ from __future__ import annotations from typing import Any import logging import copy -from collections.abc import Mapping +from collections.abc import Mapping # pylint: disable=import-error import voluptuous as vol from homeassistant.exceptions import HomeAssistantError diff --git a/custom_components/versatile_thermostat/config_schema.py b/custom_components/versatile_thermostat/config_schema.py index 92fba1f..a248f90 100644 --- a/custom_components/versatile_thermostat/config_schema.py +++ b/custom_components/versatile_thermostat/config_schema.py @@ -368,13 +368,13 @@ STEP_PRESENCE_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name STEP_CENTRAL_ADVANCED_DATA_SCHEMA = vol.Schema( # pylint: disable=invalid-name { vol.Required(CONF_MINIMAL_ACTIVATION_DELAY, default=10): cv.positive_int, - vol.Required(CONF_SECURITY_DELAY_MIN, default=60): cv.positive_int, + vol.Required(CONF_SAFETY_DELAY_MIN, default=60): cv.positive_int, vol.Required( - CONF_SECURITY_MIN_ON_PERCENT, + CONF_SAFETY_MIN_ON_PERCENT, default=DEFAULT_SECURITY_MIN_ON_PERCENT, ): vol.Coerce(float), vol.Required( - CONF_SECURITY_DEFAULT_ON_PERCENT, + CONF_SAFETY_DEFAULT_ON_PERCENT, default=DEFAULT_SECURITY_DEFAULT_ON_PERCENT, ): vol.Coerce(float), } diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index e0b3360..f334d0a 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -29,7 +29,7 @@ from .prop_algorithm import ( _LOGGER = logging.getLogger(__name__) CONFIG_VERSION = 2 -CONFIG_MINOR_VERSION = 0 +CONFIG_MINOR_VERSION = 1 PRESET_TEMP_SUFFIX = "_temp" PRESET_AC_SUFFIX = "_ac" @@ -42,10 +42,10 @@ DEVICE_MANUFACTURER = "JMCOLLIN" DEVICE_MODEL = "Versatile Thermostat" PRESET_POWER = "power" -PRESET_SECURITY = "security" +PRESET_SAFETY = "security" PRESET_FROST_PROTECTION = "frost" -HIDDEN_PRESETS = [PRESET_POWER, PRESET_SECURITY] +HIDDEN_PRESETS = [PRESET_POWER, PRESET_SAFETY] DOMAIN = "versatile_thermostat" @@ -84,9 +84,9 @@ CONF_PRESET_POWER = "power_temp" CONF_MINIMAL_ACTIVATION_DELAY = "minimal_activation_delay" CONF_TEMP_MIN = "temp_min" CONF_TEMP_MAX = "temp_max" -CONF_SECURITY_DELAY_MIN = "security_delay_min" -CONF_SECURITY_MIN_ON_PERCENT = "security_min_on_percent" -CONF_SECURITY_DEFAULT_ON_PERCENT = "security_default_on_percent" +CONF_SAFETY_DELAY_MIN = "safety_delay_min" +CONF_SAFETY_MIN_ON_PERCENT = "safety_min_on_percent" +CONF_SAFETY_DEFAULT_ON_PERCENT = "safety_default_on_percent" CONF_THERMOSTAT_TYPE = "thermostat_type" CONF_THERMOSTAT_CENTRAL_CONFIG = "thermostat_central_config" CONF_THERMOSTAT_SWITCH = "thermostat_over_switch" @@ -286,9 +286,9 @@ ALL_CONF = ( CONF_MINIMAL_ACTIVATION_DELAY, CONF_TEMP_MIN, CONF_TEMP_MAX, - CONF_SECURITY_DELAY_MIN, - CONF_SECURITY_MIN_ON_PERCENT, - CONF_SECURITY_DEFAULT_ON_PERCENT, + CONF_SAFETY_DELAY_MIN, + CONF_SAFETY_MIN_ON_PERCENT, + CONF_SAFETY_DEFAULT_ON_PERCENT, CONF_THERMOSTAT_TYPE, CONF_THERMOSTAT_SWITCH, CONF_THERMOSTAT_CLIMATE, @@ -374,7 +374,7 @@ SUPPORT_FLAGS = ( SERVICE_SET_PRESENCE = "set_presence" SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature" -SERVICE_SET_SECURITY = "set_security" +SERVICE_SET_SAFETY = "set_safety" SERVICE_SET_WINDOW_BYPASS = "set_window_bypass" SERVICE_SET_AUTO_REGULATION_MODE = "set_auto_regulation_mode" SERVICE_SET_AUTO_FAN_MODE = "set_auto_fan_mode" diff --git a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py index b601745..3880b36 100644 --- a/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py +++ b/custom_components/versatile_thermostat/feature_auto_start_stop_manager.py @@ -211,7 +211,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager): @overrides @property def is_configured(self) -> bool: - """Return True of the window feature is configured""" + """Return True of the aiuto-start/stop feature is configured""" return self._is_configured @property diff --git a/custom_components/versatile_thermostat/feature_safety_manager.py b/custom_components/versatile_thermostat/feature_safety_manager.py new file mode 100644 index 0000000..90d4cdd --- /dev/null +++ b/custom_components/versatile_thermostat/feature_safety_manager.py @@ -0,0 +1,310 @@ +# pylint: disable=line-too-long + +""" Implements the Safety as a Feature Manager""" + +import logging +from typing import Any + +from homeassistant.const import ( + STATE_ON, + STATE_OFF, + STATE_UNAVAILABLE, + STATE_UNKNOWN, +) + +from homeassistant.core import HomeAssistant +from homeassistant.components.climate import HVACMode, HVACAction + +from .const import * # pylint: disable=wildcard-import, unused-wildcard-import +from .commons import ConfigData + +from .base_manager import BaseFeatureManager +from .vtherm_api import VersatileThermostatAPI + +_LOGGER = logging.getLogger(__name__) + + +class FeatureSafetyManager(BaseFeatureManager): + """The implementation of the Safety feature""" + + unrecorded_attributes = frozenset( + { + "safety_delay_min", + "safety_min_on_percent", + "safety_default_on_percent", + "is_safety_configured", + } + ) + + def __init__(self, vtherm: Any, hass: HomeAssistant): + """Init of a featureManager""" + super().__init__(vtherm, hass) + + self._is_configured: bool = False + self._safety_delay_min = None + self._safety_min_on_percent = None + self._safety_default_on_percent = None + self._safety_state = STATE_UNAVAILABLE + + @overrides + def post_init(self, entry_infos: ConfigData): + """Reinit of the manager""" + self._safety_delay_min = entry_infos.get(CONF_SAFETY_DELAY_MIN) + self._safety_min_on_percent = ( + entry_infos.get(CONF_SAFETY_MIN_ON_PERCENT) + if entry_infos.get(CONF_SAFETY_MIN_ON_PERCENT) is not None + else DEFAULT_SECURITY_MIN_ON_PERCENT + ) + self._safety_default_on_percent = ( + entry_infos.get(CONF_SAFETY_DEFAULT_ON_PERCENT) + if entry_infos.get(CONF_SAFETY_DEFAULT_ON_PERCENT) is not None + else DEFAULT_SECURITY_DEFAULT_ON_PERCENT + ) + + self._safety_state = STATE_UNKNOWN + self._is_configured = True + + @overrides + def start_listening(self): + """Start listening the underlying entity""" + + @overrides + def stop_listening(self): + """Stop listening and remove the eventual timer still running""" + + @overrides + async def refresh_state(self) -> bool: + """Check the safety and an eventual action + Return True is safety should be active""" + + if not self._is_configured: + _LOGGER.debug("%s - safety is disabled (or not configured)", self) + return False + + now = self._vtherm.now + current_tz = dt_util.get_time_zone(self._hass.config.time_zone) + + is_safety_detected = self.is_safety_detected + + delta_temp = ( + now - self._vtherm.last_temperature_measure.replace(tzinfo=current_tz) + ).total_seconds() / 60.0 + delta_ext_temp = ( + now - self._vtherm.last_ext_temperature_measure.replace(tzinfo=current_tz) + ).total_seconds() / 60.0 + + mode_cond = self._vtherm.hvac_mode != HVACMode.OFF + + api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api() + is_outdoor_checked = ( + not api.safety_mode + or api.safety_mode.get("check_outdoor_sensor") is not False + ) + + temp_cond: bool = delta_temp > self._safety_delay_min or ( + is_outdoor_checked and delta_ext_temp > self._safety_delay_min + ) + climate_cond: bool = ( + self._vtherm.is_over_climate + and self._vtherm.hvac_action + not in [ + HVACAction.COOLING, + HVACAction.IDLE, + ] + ) + switch_cond: bool = ( + not self._vtherm.is_over_climate + and self._vtherm.proportional_algorithm is not None + and self._vtherm.proportional_algorithm.calculated_on_percent + >= self._safety_min_on_percent + ) + + _LOGGER.debug( + "%s - checking safety delta_temp=%.1f delta_ext_temp=%.1f mod_cond=%s temp_cond=%s climate_cond=%s switch_cond=%s", + self, + delta_temp, + delta_ext_temp, + mode_cond, + temp_cond, + climate_cond, + switch_cond, + ) + + # Issue 99 - a climate is regulated by the device itself and not by VTherm. So a VTherm should never be in safety ! + should_climate_be_in_security = False # temp_cond and climate_cond + should_switch_be_in_security = temp_cond and switch_cond + should_be_in_security = ( + should_climate_be_in_security or should_switch_be_in_security + ) + + should_start_security = ( + mode_cond and not is_safety_detected and should_be_in_security + ) + # attr_preset_mode is not necessary normaly. It is just here to be sure + should_stop_security = ( + is_safety_detected + and not should_be_in_security + and self._vtherm.preset_mode == PRESET_SAFETY + ) + + # Logging and event + if should_start_security: + if should_climate_be_in_security: + _LOGGER.warning( + "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Setting it into safety mode", + self, + self._safety_delay_min, + delta_temp, + delta_ext_temp, + self.hvac_action, + ) + elif should_switch_be_in_security: + _LOGGER.warning( + "%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f %%) is over defined value (%.2f %%). Set it into safety mode", + self, + self._safety_delay_min, + delta_temp, + delta_ext_temp, + self._vtherm.proportional_algorithm.on_percent * 100, + self._safety_min_on_percent * 100, + ) + + self._vtherm.send_event( + EventType.TEMPERATURE_EVENT, + { + "last_temperature_measure": self._vtherm.last_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "current_temp": self._vtherm.current_temperature, + "current_ext_temp": self._vtherm.current_outdoor_temperature, + "target_temp": self._vtherm.target_temperature, + }, + ) + + # Start safety mode + if should_start_security: + self._safety_state = STATE_ON + self._vtherm.save_hvac_mode() + self._vtherm.save_preset_mode() + if self._vtherm.proportional_algorithm: + self._vtherm.proportional_algorithm.set_safety( + self._safety_default_on_percent + ) + await self._vtherm.async_set_preset_mode_internal(PRESET_SAFETY) + # Turn off the underlying climate or heater if safety default on_percent is 0 + if self._vtherm.is_over_climate or self._safety_default_on_percent <= 0.0: + await self._vtherm.async_set_hvac_mode(HVACMode.OFF, False) + + self._vtherm.send_event( + EventType.SECURITY_EVENT, + { + "type": "start", + "last_temperature_measure": self._vtherm.last_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "current_temp": self._vtherm.current_temperature, + "current_ext_temp": self._vtherm.current_outdoor_temperature, + "target_temp": self._vtherm.target_temperature, + }, + ) + + # Stop safety mode + elif should_stop_security: + _LOGGER.warning( + "%s - End of safety mode. restoring hvac_mode to %s and preset_mode to %s", + self, + self._vtherm.saved_hvac_mode, + self._vtherm.saved_preset_mode, + ) + self._safety_state = STATE_OFF + if self._vtherm.proportional_algorithm: + self._vtherm.proportional_algorithm.unset_safety() + # Restore hvac_mode if previously saved + if self._vtherm.is_over_climate or self._safety_default_on_percent <= 0.0: + await self._vtherm.restore_hvac_mode(False) + await self._vtherm.restore_preset_mode() + self._vtherm.send_event( + EventType.SECURITY_EVENT, + { + "type": "end", + "last_temperature_measure": self._vtherm.last_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "last_ext_temperature_measure": self._vtherm.last_ext_temperature_measure.replace( + tzinfo=current_tz + ).isoformat(), + "current_temp": self._vtherm.current_temperature, + "current_ext_temp": self._vtherm.current_outdoor_temperature, + "target_temp": self._vtherm.target_temperature, + }, + ) + + # Initialize the safety_state if not already done + elif not should_be_in_security and self._safety_state in [STATE_UNKNOWN]: + self._safety_state = STATE_OFF + + return should_be_in_security + + def add_custom_attributes(self, extra_state_attributes: dict[str, Any]): + """Add some custom attributes""" + extra_state_attributes.update( + { + "safety_delay_min": self._safety_delay_min, + "safety_min_on_percent": self._safety_min_on_percent, + "safety_default_on_percent": self._safety_default_on_percent, + "safety_state": self._safety_state, + "is_safety_configured": self._is_configured, + } + ) + + @overrides + @property + def is_configured(self) -> bool: + """Return True of the safety feature is configured""" + return self._is_configured + + def set_safety_delay_min(self, safety_delay_min): + """Set the delay min""" + self._safety_delay_min = safety_delay_min + + def set_safety_min_on_percent(self, safety_min_on_percent): + """Set the min on percent""" + self._safety_min_on_percent = safety_min_on_percent + + def set_safety_default_on_percent(self, safety_default_on_percent): + """Set the default on_percent""" + self._safety_default_on_percent = safety_default_on_percent + + @property + def is_safety_detected(self) -> bool: + """Returns the is vtherm is in safety mode""" + return self._safety_state == STATE_ON + + @property + def safety_state(self) -> str: + """Returns the safety state: STATE_ON, STATE_OFF, STATE_UNKWNON, STATE_UNAVAILABLE""" + return self._safety_state + + @property + def safety_delay_min(self) -> bool: + """Returns the safety delay min""" + return self._safety_delay_min + + @property + def safety_min_on_percent(self) -> bool: + """Returns the safety min on percent""" + return self._safety_min_on_percent + + @property + def safety_default_on_percent(self) -> bool: + """Returns the safety safety_default_on_percent""" + return self._safety_default_on_percent + + def __str__(self): + return f"SafetyManager-{self.name}" diff --git a/custom_components/versatile_thermostat/prop_algorithm.py b/custom_components/versatile_thermostat/prop_algorithm.py index 2feb26b..f37747f 100644 --- a/custom_components/versatile_thermostat/prop_algorithm.py +++ b/custom_components/versatile_thermostat/prop_algorithm.py @@ -187,7 +187,7 @@ class PropAlgorithm: self._off_time_sec = self._cycle_min * 60 - self._on_time_sec - def set_security(self, default_on_percent: float): + def set_safety(self, default_on_percent: float): """Set a default value for on_percent (used for safety mode)""" _LOGGER.info( "%s - Proportional Algo - set security to ON", self._vtherm_entity_id @@ -196,7 +196,7 @@ class PropAlgorithm: self._default_on_percent = default_on_percent self._calculate_internal() - def unset_security(self): + def unset_safety(self): """Unset the safety mode""" _LOGGER.info( "%s - Proportional Algo - set security to OFF", self._vtherm_entity_id diff --git a/custom_components/versatile_thermostat/services.yaml b/custom_components/versatile_thermostat/services.yaml index 2168fa0..82da3ad 100644 --- a/custom_components/versatile_thermostat/services.yaml +++ b/custom_components/versatile_thermostat/services.yaml @@ -76,7 +76,7 @@ set_preset_temperature: unit_of_measurement: ° mode: slider -set_security: +set_safety: name: Set safety description: Change the safety parameters target: diff --git a/custom_components/versatile_thermostat/underlyings.py b/custom_components/versatile_thermostat/underlyings.py index 84b9641..a79a918 100644 --- a/custom_components/versatile_thermostat/underlyings.py +++ b/custom_components/versatile_thermostat/underlyings.py @@ -413,7 +413,7 @@ class UnderlyingSwitch(UnderlyingEntity): _LOGGER.debug("%s - End of cycle (3)", self) return # safety mode could have change the on_time percent - await self._thermostat.check_safety() + await self._thermostat.safety_manager.refresh_state() time = self._on_time_sec action_label = "start" diff --git a/documentation/en/one-page.md b/documentation/en/one-page.md index bbdd25b..0f6eb83 100644 --- a/documentation/en/one-page.md +++ b/documentation/en/one-page.md @@ -1053,7 +1053,7 @@ Si le thermostat est en mode ``security`` les nouveaux paramètres sont appliqu Pour changer les paramètres de sécurité utilisez le code suivant : ``` -service : versatile_thermostat.set_security +service : versatile_thermostat.set_safety data: min_on_percent: "0.5" default_on_percent: "0.1" diff --git a/documentation/en/reference.md b/documentation/en/reference.md index 7ee823a..f3209b4 100644 --- a/documentation/en/reference.md +++ b/documentation/en/reference.md @@ -175,7 +175,7 @@ If the thermostat is in ``security`` mode, the new settings are applied immediat To change the security settings, use the following code: ```yaml -service: versatile_thermostat.set_security +service: versatile_thermostat.set_safety data: min_on_percent: "0.5" default_on_percent: "0.1" diff --git a/documentation/fr/one-page.md b/documentation/fr/one-page.md index bbdd25b..0f6eb83 100644 --- a/documentation/fr/one-page.md +++ b/documentation/fr/one-page.md @@ -1053,7 +1053,7 @@ Si le thermostat est en mode ``security`` les nouveaux paramètres sont appliqu Pour changer les paramètres de sécurité utilisez le code suivant : ``` -service : versatile_thermostat.set_security +service : versatile_thermostat.set_safety data: min_on_percent: "0.5" default_on_percent: "0.1" diff --git a/documentation/fr/reference.md b/documentation/fr/reference.md index dfd0716..1fd3dab 100644 --- a/documentation/fr/reference.md +++ b/documentation/fr/reference.md @@ -173,7 +173,7 @@ Si le thermostat est en mode ``security`` les nouveaux paramètres sont appliqu Pour changer les paramètres de sécurité utilisez le code suivant : ```yaml -service : versatile_thermostat.set_security +service : versatile_thermostat.set_safety data: min_on_percent: "0.5" default_on_percent: "0.1" diff --git a/tests/commons.py b/tests/commons.py index 371cdec..1b12423 100644 --- a/tests/commons.py +++ b/tests/commons.py @@ -197,9 +197,9 @@ FULL_CENTRAL_CONFIG = { CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, } @@ -238,9 +238,9 @@ FULL_CENTRAL_CONFIG_WITH_BOILER = { CONF_MAX_POWER_SENSOR: "sensor.mock_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: True, CONF_CENTRAL_BOILER_ACTIVATION_SRV: "switch.pompe_chaudiere/switch.turn_on", CONF_CENTRAL_BOILER_DEACTIVATION_SRV: "switch.pompe_chaudiere/switch.turn_off", diff --git a/tests/const.py b/tests/const.py index 5214c22..0940ddd 100644 --- a/tests/const.py +++ b/tests/const.py @@ -89,7 +89,12 @@ MOCK_TH_OVER_SWITCH_AC_TYPE_CONFIG = { } MOCK_TH_OVER_4SWITCH_TYPE_CONFIG = { - CONF_UNDERLYING_LIST: ["switch.mock_4switch0", "switch.mock_4switch1","switch.mock_4switch2","switch.mock_4switch3"], + CONF_UNDERLYING_LIST: [ + "switch.mock_4switch0", + "switch.mock_4switch1", + "switch.mock_4switch2", + "switch.mock_4switch3", + ], CONF_HEATER_KEEP_ALIVE: 0, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_AC_MODE: False, @@ -195,9 +200,9 @@ MOCK_PRESENCE_AC_CONFIG = { MOCK_ADVANCED_CONFIG = { CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, } MOCK_DEFAULT_FEATURE_CONFIG = { diff --git a/tests/test_auto_fan_mode.py b/tests/test_auto_fan_mode.py index 768ed33..a7271b5 100644 --- a/tests/test_auto_fan_mode.py +++ b/tests/test_auto_fan_mode.py @@ -53,8 +53,8 @@ async def test_over_climate_auto_fan_mode_turbo( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, }, ) @@ -119,8 +119,8 @@ async def test_over_climate_auto_fan_mode_not_turbo( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, }, ) @@ -189,8 +189,8 @@ async def test_over_climate_auto_fan_mode_turbo_activation( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, }, diff --git a/tests/test_auto_start_stop.py b/tests/test_auto_start_stop.py index 4d5e274..62ede37 100644 --- a/tests/test_auto_start_stop.py +++ b/tests/test_auto_start_stop.py @@ -335,8 +335,8 @@ async def test_auto_start_stop_none_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_NONE, @@ -426,8 +426,8 @@ async def test_auto_start_stop_medium_heat_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_MEDIUM, @@ -691,8 +691,8 @@ async def test_auto_start_stop_fast_ac_vtherm( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_UNDERLYING_LIST: ["climate.mock_climate"], CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -932,8 +932,8 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -1145,8 +1145,8 @@ async def test_auto_start_stop_medium_heat_vtherm_preset_change_enable_false( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -1284,8 +1284,8 @@ async def test_auto_start_stop_fast_heat_window( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -1462,8 +1462,8 @@ async def test_auto_start_stop_fast_heat_window_mixed( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -1644,8 +1644,8 @@ async def test_auto_start_stop_disable_vtherm_off( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: False, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, diff --git a/tests/test_binary_sensors.py b/tests/test_binary_sensors.py index 66cbe27..68788c9 100644 --- a/tests/test_binary_sensors.py +++ b/tests/test_binary_sensors.py @@ -57,8 +57,8 @@ async def test_security_binary_sensors( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) @@ -87,14 +87,14 @@ async def test_security_binary_sensors( # set temperature to 15 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 15, event_timestamp) - assert entity.security_state is True + assert entity.safety_state is STATE_ON # Simulate the event reception await security_binary_sensor.async_my_climate_changed() assert security_binary_sensor.state == STATE_ON # set temperature now await send_temperature_change_event(entity, 15, now) - assert entity.security_state is False + assert entity.safety_state is not STATE_ON # Simulate the event reception await security_binary_sensor.async_my_climate_changed() assert security_binary_sensor.state == STATE_OFF @@ -134,8 +134,8 @@ async def test_overpowering_binary_sensors( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, @@ -218,8 +218,8 @@ async def test_window_binary_sensors( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait }, @@ -306,8 +306,8 @@ async def test_motion_binary_sensors( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 0, # important to not been obliged to wait CONF_MOTION_PRESET: PRESET_BOOST, @@ -399,8 +399,8 @@ async def test_presence_binary_sensors( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PRESENCE_SENSOR: "binary_sensor.mock_presence_sensor", }, ) @@ -482,8 +482,8 @@ async def test_binary_sensors_over_climate_minimal( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) diff --git a/tests/test_bugs.py b/tests/test_bugs.py index 44df601..d3a88e4 100644 --- a/tests/test_bugs.py +++ b/tests/test_bugs.py @@ -63,9 +63,9 @@ async def test_bug_63( 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.0, # !! here - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.0, # !! here + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.0, # !! here + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.0, # !! here CONF_DEVICE_POWER: 200, }, ) @@ -75,8 +75,8 @@ async def test_bug_63( ) assert entity - assert entity._security_min_on_percent == 0 - assert entity._security_default_on_percent == 0 + assert entity.safety_manager.safety_min_on_percent == 0 + assert entity.safety_manager.safety_default_on_percent == 0 # Waiting for answer in https://github.com/jmcollin78/versatile_thermostat/issues/64 @@ -115,9 +115,9 @@ async def test_bug_64( 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.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1, # !! here + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, # !! here CONF_DEVICE_POWER: 200, }, ) @@ -299,8 +299,8 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state): 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, @@ -510,8 +510,8 @@ async def test_bug_465(hass: HomeAssistant, skip_hass_states_is_state): CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, }, diff --git a/tests/test_central_boiler.py b/tests/test_central_boiler.py index 4f66432..4ccb0a5 100644 --- a/tests/test_central_boiler.py +++ b/tests/test_central_boiler.py @@ -111,9 +111,9 @@ async def test_update_central_boiler_state_simple( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_TPI_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, @@ -298,9 +298,9 @@ async def test_update_central_boiler_state_multiple( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_TPI_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, @@ -626,9 +626,9 @@ async def test_update_central_boiler_state_simple_valve( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_TPI_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, @@ -800,9 +800,9 @@ async def test_update_central_boiler_state_simple_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_UNDERLYING_LIST: [climate1.entity_id], 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, CONF_USE_ADVANCED_CENTRAL_CONFIG: True, @@ -990,9 +990,9 @@ async def test_update_central_boiler_state_simple_climate_valve_regulation( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, CONF_USE_ADVANCED_CENTRAL_CONFIG: True, @@ -1255,9 +1255,9 @@ async def test_bug_339( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: climate1.entity_id, 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_USE_MAIN_CENTRAL_CONFIG: True, CONF_USE_PRESETS_CENTRAL_CONFIG: True, CONF_USE_ADVANCED_CENTRAL_CONFIG: True, diff --git a/tests/test_central_config.py b/tests/test_central_config.py index 8491c36..11cf3bb 100644 --- a/tests/test_central_config.py +++ b/tests/test_central_config.py @@ -67,9 +67,9 @@ async def test_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_sta CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, }, ) @@ -135,9 +135,9 @@ async def test_minimal_over_switch_wo_central_config( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, # CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, # CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1, # CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test @@ -174,9 +174,9 @@ async def test_minimal_over_switch_wo_central_config( assert entity.proportional_algorithm._tpi_coef_int == 0.3 assert entity.proportional_algorithm._tpi_coef_ext == 0.01 assert entity.proportional_algorithm._minimal_activation_delay == 30 - assert entity._security_delay_min == 5 - assert entity._security_min_on_percent == 0.3 - assert entity._security_default_on_percent == 0.1 + assert entity.safety_manager.safety_delay_min == 5 + assert entity.safety_manager.safety_min_on_percent == 0.3 + assert entity.safety_manager.safety_default_on_percent == 0.1 assert entity.is_inversed entity.remove_thermostat() @@ -220,9 +220,9 @@ async def test_full_over_switch_wo_central_config( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 30, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 3, @@ -274,9 +274,9 @@ async def test_full_over_switch_wo_central_config( assert entity.proportional_algorithm._tpi_coef_int == 0.3 assert entity.proportional_algorithm._tpi_coef_ext == 0.01 assert entity.proportional_algorithm._minimal_activation_delay == 30 - assert entity._security_delay_min == 5 - assert entity._security_min_on_percent == 0.3 - assert entity._security_default_on_percent == 0.1 + assert entity.safety_manager.safety_delay_min == 5 + assert entity.safety_manager.safety_min_on_percent == 0.3 + assert entity.safety_manager.safety_default_on_percent == 0.1 assert entity.is_inversed is False assert ( @@ -348,9 +348,9 @@ async def test_full_over_switch_with_central_config( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 30, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 3, @@ -401,9 +401,9 @@ async def test_full_over_switch_with_central_config( assert entity.proportional_algorithm._tpi_coef_int == 0.5 assert entity.proportional_algorithm._tpi_coef_ext == 0.02 assert entity.proportional_algorithm._minimal_activation_delay == 11 - assert entity._security_delay_min == 61 - assert entity._security_min_on_percent == 0.5 - assert entity._security_default_on_percent == 0.2 + assert entity.safety_manager.safety_delay_min == 61 + assert entity.safety_manager.safety_min_on_percent == 0.5 + assert entity.safety_manager.safety_default_on_percent == 0.2 assert entity.is_inversed is False # We have an entity so window auto is not enabled @@ -495,7 +495,7 @@ async def test_migration_of_central_config( central_config_entry = MockConfigEntry( version=CONFIG_VERSION, # An old minor version - minor_version=1, + minor_version=0, domain=DOMAIN, title="TheCentralConfigMockName", unique_id="centralConfigUniqueId", @@ -509,9 +509,9 @@ async def test_migration_of_central_config( CONF_TPI_COEF_INT: 0.5, CONF_TPI_COEF_EXT: 0.02, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, # The old central_boiler parameter "add_central_boiler_control": True, CONF_CENTRAL_BOILER_ACTIVATION_SRV: "switch.pompe_chaudiere/switch.turn_on", diff --git a/tests/test_central_mode.py b/tests/test_central_mode.py index 28e1903..742aaef 100644 --- a/tests/test_central_mode.py +++ b/tests/test_central_mode.py @@ -56,9 +56,9 @@ async def test_config_with_central_mode_true( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -103,9 +103,9 @@ async def test_config_with_central_mode_false( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -153,9 +153,9 @@ async def test_config_with_central_mode_none( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -205,9 +205,9 @@ async def test_switch_change_central_mode_true( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -347,9 +347,9 @@ async def test_switch_ac_change_central_mode_true( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_AC_MODE: True, }, ) @@ -482,9 +482,9 @@ async def test_climate_ac_change_central_mode_false( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -624,9 +624,9 @@ async def test_climate_ac_only_change_central_mode_true( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -778,9 +778,9 @@ async def test_switch_change_central_mode_true_with_window( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_WINDOW_SENSOR: "binary_sensor.window_sensor", CONF_WINDOW_DELAY: 0, # To be not obliged to wait CONF_MOTION_SENSOR: "input_boolean.motion_sensor", @@ -935,9 +935,9 @@ async def test_switch_change_central_mode_true_with_cool_only_and_window( 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_SECURITY_DEFAULT_ON_PERCENT: 0.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, CONF_WINDOW_SENSOR: "binary_sensor.window_sensor", CONF_WINDOW_DELAY: 0, # To be not obliged to wait CONF_MOTION_SENSOR: "input_boolean.motion_sensor", diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index 14c8d99..7e1f4d7 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -470,9 +470,9 @@ async def test_user_config_flow_over_climate( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, }, ) assert result["type"] == FlowResultType.MENU @@ -496,9 +496,9 @@ async def test_user_config_flow_over_climate( "data" ] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, } | MOCK_DEFAULT_FEATURE_CONFIG | { CONF_USE_MAIN_CENTRAL_CONFIG: False, CONF_USE_PRESETS_CENTRAL_CONFIG: False, @@ -1077,9 +1077,9 @@ async def test_user_config_flow_over_climate_auto_start_stop( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, }, ) assert result["type"] == FlowResultType.MENU @@ -1104,9 +1104,9 @@ async def test_user_config_flow_over_climate_auto_start_stop( "data" ] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, } | MOCK_DEFAULT_FEATURE_CONFIG | { CONF_USE_MAIN_CENTRAL_CONFIG: False, CONF_USE_TPI_CENTRAL_CONFIG: False, @@ -1274,9 +1274,9 @@ async def test_user_config_flow_over_switch_bug_552_tpi( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, }, ) @@ -1359,9 +1359,9 @@ async def test_user_config_flow_over_switch_bug_552_tpi( CONF_TEMP_MAX: 30, CONF_STEP_TEMPERATURE: 0.5, CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, CONF_USE_MAIN_CENTRAL_CONFIG: False, CONF_USE_TPI_CENTRAL_CONFIG: False, CONF_USE_PRESETS_CENTRAL_CONFIG: False, @@ -1657,9 +1657,9 @@ async def test_user_config_flow_over_climate_valve( result["flow_id"], user_input={ CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, }, ) assert result["type"] == FlowResultType.MENU @@ -1685,9 +1685,9 @@ async def test_user_config_flow_over_climate_valve( "data" ] == MOCK_TH_OVER_CLIMATE_USER_CONFIG | MOCK_TH_OVER_CLIMATE_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_CENTRAL_MAIN_CONFIG | MOCK_TH_OVER_CLIMATE_TYPE_CONFIG | { CONF_MINIMAL_ACTIVATION_DELAY: 10, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.4, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.4, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.3, } | MOCK_DEFAULT_FEATURE_CONFIG | { CONF_USE_MAIN_CENTRAL_CONFIG: False, CONF_USE_PRESETS_CENTRAL_CONFIG: False, diff --git a/tests/test_inverted_switch.py b/tests/test_inverted_switch.py index b0b8834..46f9d72 100644 --- a/tests/test_inverted_switch.py +++ b/tests/test_inverted_switch.py @@ -42,8 +42,8 @@ async def test_inverted_switch(hass: HomeAssistant, skip_hass_states_is_state): 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1, CONF_WINDOW_AUTO_MAX_DURATION: 0, # Should be 0 for test diff --git a/tests/test_last_seen.py b/tests/test_last_seen.py index 906023c..5be3b21 100644 --- a/tests/test_last_seen.py +++ b/tests/test_last_seen.py @@ -25,6 +25,12 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) """ tz = get_tz(hass) # pylint: disable=invalid-name + temps = { + "frost": 7, + "eco": 17, + "comfort": 18, + "boost": 19, + } entry = MockConfigEntry( domain=DOMAIN, @@ -39,22 +45,18 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) "cycle_min": 5, "temp_min": 15, "temp_max": 30, - "frost_temp": 7, - "eco_temp": 17, - "comfort_temp": 18, - "boost_temp": 19, "use_window_feature": False, "use_motion_feature": False, "use_power_feature": False, "use_presence_feature": False, - "heater_entity_id": "switch.mock_switch", + CONF_UNDERLYING_LIST: ["switch.mock_switch"], "proportional_function": "tpi", "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, "minimal_activation_delay": 30, - "security_delay_min": 5, # 5 minutes - "security_min_on_percent": 0.2, - "security_default_on_percent": 0.1, + CONF_SAFETY_DELAY_MIN: 5, # 5 minutes + CONF_SAFETY_MIN_ON_PERCENT: 0.2, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, }, ) @@ -65,8 +67,10 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) ) assert entity - assert entity._security_state is False - assert entity.preset_mode is not PRESET_SECURITY + await set_all_climate_preset_temp(hass, entity, temps, "theoverswitchmockname") + + assert entity.safety_manager.is_safety_detected is False + assert entity.preset_mode is not PRESET_SAFETY assert entity._last_ext_temperature_measure is not None assert entity._last_temperature_measure is not None assert (entity._last_temperature_measure.astimezone(tz) - now).total_seconds() < 1 @@ -96,13 +100,13 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) # set temperature to 15 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 15, event_timestamp) - assert entity.security_state is True - assert entity.preset_mode == PRESET_SECURITY + assert entity.safety_state is STATE_ON + assert entity.preset_mode == PRESET_SAFETY assert mock_send_event.call_count == 3 mock_send_event.assert_has_calls( [ - call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SECURITY}), + call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SAFETY}), call.send_event( EventType.TEMPERATURE_EVENT, { @@ -135,7 +139,7 @@ async def test_last_seen_feature(hass: HomeAssistant, skip_hass_states_is_state) # 3. change the last seen sensor event_timestamp = now - timedelta(minutes=4) await send_last_seen_temperature_change_event(entity, event_timestamp) - assert entity.security_state is False + assert entity.safety_state is not STATE_ON assert entity.preset_mode is PRESET_COMFORT assert entity._last_temperature_measure == event_timestamp diff --git a/tests/test_motion.py b/tests/test_motion.py index f2104d6..a89a54d 100644 --- a/tests/test_motion.py +++ b/tests/test_motion.py @@ -305,8 +305,8 @@ async def test_motion_management_time_not_enough( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 10, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 10, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 10, # important to not been obliged to wait CONF_MOTION_OFF_DELAY: 30, @@ -522,8 +522,8 @@ async def test_motion_management_time_enough_and_presence( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 0, # important to not been obliged to wait CONF_MOTION_PRESET: "boost", @@ -653,8 +653,8 @@ async def test_motion_management_time_enough_and_not_presence( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 0, # important to not been obliged to wait CONF_MOTION_PRESET: "boost", @@ -785,8 +785,8 @@ async def test_motion_management_with_stop_during_condition( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 10, CONF_MOTION_OFF_DELAY: 30, @@ -921,8 +921,8 @@ async def test_motion_management_with_stop_during_condition_last_state_on( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_MOTION_SENSOR: "binary_sensor.mock_motion_sensor", CONF_MOTION_DELAY: 10, CONF_MOTION_OFF_DELAY: 30, diff --git a/tests/test_multiple_switch.py b/tests/test_multiple_switch.py index 08455ed..e0e8583 100644 --- a/tests/test_multiple_switch.py +++ b/tests/test_multiple_switch.py @@ -45,8 +45,8 @@ async def test_one_switch_cycle( CONF_USE_PRESENCE_FEATURE: False, CONF_HEATER: "switch.mock_switch1", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, @@ -262,8 +262,8 @@ async def test_multiple_switchs( CONF_HEATER_4: "switch.mock_switch4", CONF_HEATER_KEEP_ALIVE: 0, CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, @@ -402,8 +402,8 @@ async def test_multiple_climates( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) @@ -503,8 +503,8 @@ async def test_multiple_climates_underlying_changes( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) @@ -648,8 +648,8 @@ async def test_multiple_climates_underlying_changes_not_aligned( CONF_CLIMATE_3: "switch.mock_climate3", CONF_CLIMATE_4: "switch.mock_climate4", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) @@ -750,8 +750,8 @@ async def test_multiple_switch_power_management( CONF_HEATER_4: "switch.mock_switch4", CONF_HEATER_KEEP_ALIVE: 0, CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, diff --git a/tests/test_overclimate.py b/tests/test_overclimate.py index dd7898a..b6884c5 100644 --- a/tests/test_overclimate.py +++ b/tests/test_overclimate.py @@ -62,8 +62,8 @@ async def test_bug_56( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) @@ -151,7 +151,7 @@ async def test_bug_82( PRESET_BOOST, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False + assert entity.safety_manager.is_safety_detected is False # should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT assert mock_send_event.call_count == 2 @@ -194,7 +194,7 @@ async def test_bug_82( # set temperature to 15 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 15, event_timestamp) # Should stay False - assert entity.security_state is False + assert entity.safety_state is not STATE_ON assert entity.preset_mode == "none" assert entity._saved_preset_mode == "none" @@ -641,8 +641,8 @@ async def test_bug_524(hass: HomeAssistant, skip_hass_states_is_state): CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, }, @@ -904,8 +904,8 @@ async def test_manual_hvac_off_should_take_the_lead_over_window( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, @@ -1081,8 +1081,8 @@ async def test_manual_hvac_off_should_take_the_lead_over_auto_start_stop( CONF_PRESENCE_SENSOR: "binary_sensor.presence_sensor", CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_AUTO_FAN_MODE: CONF_AUTO_FAN_TURBO, CONF_AC_MODE: True, CONF_AUTO_START_STOP_LEVEL: AUTO_START_STOP_LEVEL_FAST, diff --git a/tests/test_overclimate_valve.py b/tests/test_overclimate_valve.py index 131460c..cbfcbd9 100644 --- a/tests/test_overclimate_valve.py +++ b/tests/test_overclimate_valve.py @@ -111,7 +111,7 @@ async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get PRESET_BOOST, ] assert vtherm.preset_mode is PRESET_NONE - assert vtherm._security_state is False + assert vtherm.safety_manager.is_safety_detected is False assert vtherm.window_state is STATE_UNAVAILABLE assert vtherm.motion_state is STATE_UNAVAILABLE assert vtherm.presence_state is STATE_UNAVAILABLE diff --git a/tests/test_power.py b/tests/test_power.py index 366ea5b..c2b1828 100644 --- a/tests/test_power.py +++ b/tests/test_power.py @@ -269,8 +269,8 @@ async def test_power_management_hvac_off( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, @@ -355,8 +355,8 @@ async def test_power_management_hvac_on(hass: HomeAssistant, skip_hass_states_is 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, @@ -491,8 +491,8 @@ async def test_power_management_energy_over_switch( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, @@ -620,8 +620,8 @@ async def test_power_management_energy_over_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 100, diff --git a/tests/test_security.py b/tests/test_safety.py similarity index 83% rename from tests/test_security.py rename to tests/test_safety.py index 1d37b19..3d83e66 100644 --- a/tests/test_security.py +++ b/tests/test_safety.py @@ -24,17 +24,26 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): 1. creates a thermostat and check that security is off 2. activate security feature when date is expired 3. change the preset to boost - 4. check that security is still on 5. resolve the date issue 6. check that security is off and preset is changed to boost """ tz = get_tz(hass) # pylint: disable=invalid-name + temps = { + "frost": 7, + "eco": 17, + "comfort": 18, + "boost": 19, + } + entry = MockConfigEntry( domain=DOMAIN, title="TheOverSwitchMockName", unique_id="uniqueId", + # With migration + version=CONFIG_VERSION, + minor_version=0, data={ "name": "TheOverSwitchMockName", "thermostat_type": "thermostat_over_switch", @@ -43,15 +52,11 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): "cycle_min": 5, "temp_min": 15, "temp_max": 30, - "frost_temp": 7, - "eco_temp": 17, - "comfort_temp": 18, - "boost_temp": 19, "use_window_feature": False, "use_motion_feature": False, "use_power_feature": False, "use_presence_feature": False, - "heater_entity_id": "switch.mock_switch", + CONF_UNDERLYING_LIST: ["switch.mock_switch"], "proportional_function": "tpi", "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, @@ -69,8 +74,11 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): ) assert entity - assert entity._security_state is False - assert entity.preset_mode is not PRESET_SECURITY + await set_all_climate_preset_temp(hass, entity, temps, "theoverswitchmockname") + + assert entity.safety_manager.safety_state is not STATE_ON + assert entity.safety_manager.is_safety_detected is False + assert entity.preset_mode is not PRESET_SAFETY assert entity.preset_modes == [ PRESET_NONE, PRESET_FROST_PROTECTION, @@ -105,8 +113,8 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): # set temperature to 15 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 15, event_timestamp) - assert entity.security_state is True - assert entity.preset_mode == PRESET_SECURITY + assert entity.safety_state is STATE_ON + assert entity.preset_mode == PRESET_SAFETY assert entity._saved_preset_mode == PRESET_COMFORT assert entity._prop_algorithm.on_percent == 0.1 assert entity._prop_algorithm.calculated_on_percent == 0.9 @@ -114,7 +122,7 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): assert mock_send_event.call_count == 3 mock_send_event.assert_has_calls( [ - call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SECURITY}), + call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SAFETY}), call.send_event( EventType.TEMPERATURE_EVENT, { @@ -151,11 +159,13 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): await entity.async_set_preset_mode(PRESET_BOOST) # 4. check that security is still on - assert entity._security_state is True + assert entity.safety_manager.safety_state is STATE_ON + assert entity.safety_manager.is_safety_detected is True + assert entity._prop_algorithm.on_percent == 0.1 assert entity._prop_algorithm.calculated_on_percent == 0.9 assert entity._saved_preset_mode == PRESET_BOOST - assert entity.preset_mode is PRESET_SECURITY + assert entity.preset_mode is PRESET_SAFETY # 5. resolve the datetime issue with patch( @@ -168,7 +178,9 @@ async def test_security_feature(hass: HomeAssistant, skip_hass_states_is_state): # set temperature to 15 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 15.2, event_timestamp) - assert entity._security_state is False + assert entity.safety_manager.safety_state is not STATE_ON + assert entity.safety_manager.is_safety_detected is False + assert entity.preset_mode == PRESET_BOOST assert entity._saved_preset_mode == PRESET_BOOST assert entity._prop_algorithm.on_percent == 1.0 @@ -215,6 +227,12 @@ async def test_security_feature_back_on_percent( """ tz = get_tz(hass) # pylint: disable=invalid-name + temps = { + "eco": 17, + "comfort": 18, + "boost": 19, + "frost": 10, + } entry = MockConfigEntry( domain=DOMAIN, @@ -228,21 +246,18 @@ async def test_security_feature_back_on_percent( "cycle_min": 5, "temp_min": 15, "temp_max": 30, - "eco_temp": 17, - "comfort_temp": 18, - "boost_temp": 19, "use_window_feature": False, "use_motion_feature": False, "use_power_feature": False, "use_presence_feature": False, - "heater_entity_id": "switch.mock_switch", + CONF_UNDERLYING_LIST: ["switch.mock_switch"], "proportional_function": "tpi", "tpi_coef_int": 0.3, "tpi_coef_ext": 0.01, "minimal_activation_delay": 30, - "security_delay_min": 5, # 5 minutes - "security_min_on_percent": 0.2, - "security_default_on_percent": 0.1, + "safety_delay_min": 5, # 5 minutes + "safety_min_on_percent": 0.2, + "safety_default_on_percent": 0.1, }, ) @@ -253,8 +268,12 @@ async def test_security_feature_back_on_percent( ) assert entity - assert entity._security_state is False - assert entity.preset_mode is not PRESET_SECURITY + await set_all_climate_preset_temp(hass, entity, temps, "theoverswitchmockname") + + assert entity.safety_manager.safety_state is not STATE_ON + assert entity.safety_manager.is_safety_detected is False + + assert entity.preset_mode is not PRESET_SAFETY assert entity._last_ext_temperature_measure is not None assert entity._last_temperature_measure is not None assert (entity._last_temperature_measure.astimezone(tz) - now).total_seconds() < 1 @@ -285,7 +304,7 @@ async def test_security_feature_back_on_percent( await send_temperature_change_event(entity, 17, event_timestamp) assert entity._prop_algorithm.calculated_on_percent == 0.6 assert entity.preset_mode == PRESET_BOOST - assert entity.security_state is False + assert entity.safety_state is not STATE_ON assert mock_send_event.call_count == 0 # 3. Set safety mode with a preset change @@ -302,14 +321,14 @@ async def test_security_feature_back_on_percent( assert entity._prop_algorithm.calculated_on_percent == 0.6 - assert entity.security_state is True - assert entity.preset_mode == PRESET_SECURITY + assert entity.safety_state is STATE_ON + assert entity.preset_mode == PRESET_SAFETY assert entity._saved_preset_mode == PRESET_BOOST assert mock_send_event.call_count == 3 mock_send_event.assert_has_calls( [ - call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SECURITY}), + call.send_event(EventType.PRESET_EVENT, {"preset": PRESET_SAFETY}), call.send_event( EventType.TEMPERATURE_EVENT, { @@ -343,8 +362,8 @@ async def test_security_feature_back_on_percent( entity._set_now(event_timestamp) # pylint: disable=protected-access await entity.async_set_preset_mode(PRESET_ECO) - assert entity.security_state is True - assert entity.preset_mode == PRESET_SECURITY + assert entity.safety_state is STATE_ON + assert entity.preset_mode == PRESET_SAFETY # 5. resolve the datetime issue with patch( @@ -359,7 +378,9 @@ async def test_security_feature_back_on_percent( # set temperature to 18.9 so that on_percent will be > security_min_on_percent (0.2) await send_temperature_change_event(entity, 18.92, event_timestamp) - assert entity._security_state is False + assert entity.safety_manager.safety_state is not STATE_ON + assert entity.safety_manager.is_safety_detected is False + assert entity.preset_mode == PRESET_ECO assert entity._saved_preset_mode == PRESET_ECO assert entity._prop_algorithm.on_percent == 0.0 @@ -452,7 +473,8 @@ async def test_security_over_climate( PRESET_BOOST, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False + assert entity.safety_manager.safety_state is not STATE_ON + assert entity.safety_manager.is_safety_detected is False # should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT assert mock_send_event.call_count == 2 @@ -506,6 +528,56 @@ async def test_security_over_climate( await send_temperature_change_event(entity, 15, event_timestamp) # Should stay False because a climate is never in safety mode - assert entity.security_state is False + assert entity.safety_state is not STATE_ON assert entity.preset_mode == "none" assert entity._saved_preset_mode == "none" + + +async def test_migration_security_safety( + hass: HomeAssistant, + skip_hass_states_is_state, +): + """Tests the migration of security parameters to safety in English""" + central_config_entry = MockConfigEntry( + # Current is 2.1 + version=CONFIG_VERSION, + # An old minor version + minor_version=0, + domain=DOMAIN, + title="TheCentralConfigMockName", + unique_id="centralConfigUniqueId", + data={ + CONF_NAME: "migrationName", + CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH, + CONF_UNDERLYING_LIST: ["switch.under1"], + "security_delay_min": 61, + "security_min_on_percent": 0.5, + "security_default_on_percent": 0.2, + CONF_TEMP_SENSOR: "sensor.mock_temp_sensor", + CONF_CYCLE_MIN: 5, + CONF_DEVICE_POWER: 1, + CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, + CONF_TPI_COEF_INT: 0.3, + CONF_TPI_COEF_EXT: 0.1, + CONF_USE_MAIN_CENTRAL_CONFIG: False, + CONF_USE_WINDOW_FEATURE: False, + CONF_USE_MOTION_FEATURE: False, + CONF_USE_POWER_FEATURE: False, + CONF_USE_PRESENCE_FEATURE: False, + CONF_MINIMAL_ACTIVATION_DELAY: 10, + }, + ) + + central_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(central_config_entry.entry_id) + assert central_config_entry.state is ConfigEntryState.LOADED + + entity: ThermostatOverSwitch = search_entity( + hass, "climate.migrationname", "climate" + ) + + assert entity is not None + + assert entity.safety_manager.safety_min_on_percent == 0.5 + assert entity.safety_manager.safety_default_on_percent == 0.2 + assert entity.safety_manager.safety_delay_min == 61 diff --git a/tests/test_sensors.py b/tests/test_sensors.py index 18dde95..e9a482f 100644 --- a/tests/test_sensors.py +++ b/tests/test_sensors.py @@ -62,8 +62,8 @@ async def test_sensors_over_switch( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 200, }, ) @@ -222,8 +222,8 @@ async def test_sensors_over_climate( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_POWER_SENSOR: "sensor.mock_power_sensor", CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor", CONF_DEVICE_POWER: 1.5, @@ -360,8 +360,8 @@ async def test_sensors_over_climate_minimal( CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, }, ) diff --git a/tests/test_start.py b/tests/test_start.py index 16e5d19..792c339 100644 --- a/tests/test_start.py +++ b/tests/test_start.py @@ -53,7 +53,7 @@ async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_s PRESET_ACTIVITY, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False + assert entity.safety_manager.is_safety_detected is False assert entity.window_state is STATE_UNKNOWN assert entity.motion_state is STATE_UNKNOWN assert entity.presence_state is STATE_UNKNOWN @@ -112,7 +112,7 @@ async def test_over_climate_full_start(hass: HomeAssistant, skip_hass_states_is_ PRESET_BOOST, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False + assert entity.safety_manager.is_safety_detected is False assert entity.window_state is STATE_UNAVAILABLE assert entity.motion_state is STATE_UNAVAILABLE assert entity.presence_state is STATE_UNAVAILABLE @@ -168,7 +168,7 @@ async def test_over_4switch_full_start(hass: HomeAssistant, skip_hass_states_is_ PRESET_ACTIVITY, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False + assert entity.safety_manager.is_safety_detected is False assert entity.window_state is STATE_UNKNOWN assert entity.motion_state is STATE_UNKNOWN assert entity.presence_state is STATE_UNKNOWN @@ -230,7 +230,7 @@ async def test_over_switch_deactivate_preset( CONF_HEATER_3: None, CONF_HEATER_4: None, CONF_HEATER_KEEP_ALIVE: 0, - CONF_SECURITY_DELAY_MIN: 10, + CONF_SAFETY_DELAY_MIN: 10, CONF_MINIMAL_ACTIVATION_DELAY: 10, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, CONF_TPI_COEF_INT: 0.6, diff --git a/tests/test_switch_ac.py b/tests/test_switch_ac.py index 4fc8b09..55f17ce 100644 --- a/tests/test_switch_ac.py +++ b/tests/test_switch_ac.py @@ -89,7 +89,7 @@ async def test_over_switch_ac_full_start( PRESET_ACTIVITY, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False # pylint: disable=protected-access + assert entity.safety_manager.is_safety_detected is False assert entity.window_state is STATE_UNKNOWN assert entity.motion_state is STATE_UNKNOWN assert entity.presence_state is STATE_UNKNOWN diff --git a/tests/test_switch_keep_alive.py b/tests/test_switch_keep_alive.py index c8dc846..3642742 100644 --- a/tests/test_switch_keep_alive.py +++ b/tests/test_switch_keep_alive.py @@ -39,8 +39,8 @@ def config_entry() -> MockConfigEntry: 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.1, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.1, }, ) diff --git a/tests/test_temp_number.py b/tests/test_temp_number.py index 0100cb6..7449fb1 100644 --- a/tests/test_temp_number.py +++ b/tests/test_temp_number.py @@ -75,9 +75,9 @@ async def test_add_number_for_central_config( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI, } @@ -170,9 +170,9 @@ async def test_add_number_for_central_config_without_temp( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, }, # | temps, @@ -265,9 +265,9 @@ async def test_add_number_for_central_config_without_temp_ac_mode( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, }, # | temps, @@ -359,9 +359,9 @@ async def test_add_number_for_central_config_without_temp_restore( CONF_MAX_POWER_SENSOR: "sensor.mock_central_max_power_sensor", CONF_PRESET_POWER: 14, CONF_MINIMAL_ACTIVATION_DELAY: 11, - CONF_SECURITY_DELAY_MIN: 61, - CONF_SECURITY_MIN_ON_PERCENT: 0.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.2, + CONF_SAFETY_DELAY_MIN: 61, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.2, CONF_USE_CENTRAL_BOILER_FEATURE: False, }, # | temps, diff --git a/tests/test_tpi.py b/tests/test_tpi.py index 692cbfe..a033ec5 100644 --- a/tests/test_tpi.py +++ b/tests/test_tpi.py @@ -38,8 +38,8 @@ async def test_tpi_calculation( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, # CONF_DEVICE_POWER: 100, }, ) @@ -66,14 +66,14 @@ async def test_tpi_calculation( assert tpi_algo.on_time_sec == 120 assert tpi_algo.off_time_sec == 180 - tpi_algo.set_security(0.1) + tpi_algo.set_safety(0.1) tpi_algo.calculate(15, 14, 5, HVACMode.HEAT) assert tpi_algo.on_percent == 0.1 assert tpi_algo.calculated_on_percent == 0.4 assert tpi_algo.on_time_sec == 30 # >= minimal_activation_delay (=30) assert tpi_algo.off_time_sec == 270 - tpi_algo.unset_security() + tpi_algo.unset_safety() tpi_algo.calculate(15, 14, 5, HVACMode.HEAT) assert tpi_algo.on_percent == 0.4 assert tpi_algo.calculated_on_percent == 0.4 @@ -87,14 +87,14 @@ async def test_tpi_calculation( assert tpi_algo.on_time_sec == 0 assert tpi_algo.off_time_sec == 300 - tpi_algo.set_security(0.09) + tpi_algo.set_safety(0.09) tpi_algo.calculate(15, 14.7, 15, HVACMode.HEAT) assert tpi_algo.on_percent == 0.09 assert tpi_algo.calculated_on_percent == 0.09 assert tpi_algo.on_time_sec == 0 assert tpi_algo.off_time_sec == 300 - tpi_algo.unset_security() + tpi_algo.unset_safety() tpi_algo.calculate(25, 30, 35, HVACMode.COOL) assert tpi_algo.on_percent == 1 assert tpi_algo.calculated_on_percent == 1 @@ -102,7 +102,7 @@ async def test_tpi_calculation( assert tpi_algo.off_time_sec == 0 assert entity.power_manager.mean_cycle_power is None # no device power configured - tpi_algo.set_security(0.09) + tpi_algo.set_safety(0.09) tpi_algo.calculate(25, 30, 35, HVACMode.COOL) assert tpi_algo.on_percent == 0.09 assert tpi_algo.calculated_on_percent == 1 @@ -110,7 +110,7 @@ async def test_tpi_calculation( assert tpi_algo.off_time_sec == 300 assert entity.power_manager.mean_cycle_power is None # no device power configured - tpi_algo.unset_security() + tpi_algo.unset_safety() # The calculated values for HVACMode.OFF are the same as for HVACMode.HEAT. tpi_algo.calculate(15, 10, 7, HVACMode.OFF) assert tpi_algo.on_percent == 1 diff --git a/tests/test_valve.py b/tests/test_valve.py index eb6f186..0042d0b 100644 --- a/tests/test_valve.py +++ b/tests/test_valve.py @@ -60,8 +60,8 @@ async def test_over_valve_full_start( PRESET_BOOST + PRESET_AWAY_SUFFIX + PRESET_TEMP_SUFFIX: 17.3, CONF_PRESET_POWER: 10, CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_DEVICE_POWER: 100, CONF_AC_MODE: False, }, @@ -98,7 +98,9 @@ async def test_over_valve_full_start( PRESET_ACTIVITY, ] assert entity.preset_mode is PRESET_NONE - assert entity._security_state is False # pylint: disable=protected-access + assert ( + entity.safety_manager.is_safety_detected is False + ) # pylint: disable=protected-access assert entity.window_state is STATE_UNKNOWN assert entity.motion_state is STATE_UNKNOWN assert entity.presence_state is STATE_UNKNOWN @@ -350,8 +352,8 @@ async def test_over_valve_regulation( CONF_TPI_COEF_INT: 0.3, CONF_TPI_COEF_EXT: 0.01, CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 60, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 60, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, # only send new valve open percent if dtemp is > 30% CONF_AUTO_REGULATION_DTEMP: 5, # only send new valve open percent last mesure was more than 5 min ago @@ -589,7 +591,7 @@ async def test_bug_533( CONF_VALVE: "number.mock_valve", CONF_AUTO_REGULATION_DTEMP: 10, # This parameter makes the bug CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 60, + CONF_SAFETY_DELAY_MIN: 60, }, # | temps, ) diff --git a/tests/test_window.py b/tests/test_window.py index 1de2921..29adb62 100644 --- a/tests/test_window.py +++ b/tests/test_window.py @@ -45,8 +45,8 @@ async def test_window_management_time_not_enough( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait CONF_WINDOW_ACTION: CONF_WINDOW_TURN_OFF, @@ -134,8 +134,8 @@ async def test_window_management_time_enough( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait CONF_WINDOW_ACTION: CONF_WINDOW_TURN_OFF, @@ -281,8 +281,8 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state): 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1, CONF_WINDOW_AUTO_MAX_DURATION: 10, # Should be 0 for test @@ -490,8 +490,8 @@ async def test_window_auto_fast_and_sensor( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.fake_window_sensor", CONF_WINDOW_DELAY: 10, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, @@ -615,8 +615,8 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "switch.mock_climate", CONF_MINIMAL_ACTIVATION_DELAY: 30, - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 6, CONF_WINDOW_AUTO_MAX_DURATION: 1, # 0 will deactivate window auto detection @@ -783,8 +783,8 @@ async def test_window_auto_no_on_percent( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 6, CONF_WINDOW_AUTO_MAX_DURATION: 1, # Should be 0 for test but 0 is not possible @@ -910,8 +910,8 @@ async def test_window_bypass(hass: HomeAssistant, skip_hass_states_is_state): 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait }, @@ -1050,8 +1050,8 @@ async def test_window_auto_bypass(hass: HomeAssistant, skip_hass_states_is_state 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 6, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 6, CONF_WINDOW_AUTO_MAX_DURATION: 1, # Should be > 0 to activate window_auto @@ -1178,8 +1178,8 @@ async def test_window_bypass_reactivate(hass: HomeAssistant, skip_hass_states_is 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait }, @@ -1307,8 +1307,8 @@ async def test_window_action_fan_only(hass: HomeAssistant, skip_hass_states_is_s CONF_USE_POWER_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 1, CONF_WINDOW_ACTION: CONF_WINDOW_FAN_ONLY, @@ -1464,8 +1464,8 @@ async def test_window_action_fan_only_ko( CONF_USE_POWER_FEATURE: False, CONF_USE_PRESENCE_FEATURE: False, CONF_CLIMATE: "climate.mock_climate", - CONF_SECURITY_DELAY_MIN: 5, - CONF_SECURITY_MIN_ON_PERCENT: 0.3, + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 1, CONF_WINDOW_ACTION: CONF_WINDOW_FAN_ONLY, @@ -1615,8 +1615,8 @@ async def test_window_action_eco_temp(hass: HomeAssistant, skip_hass_states_is_s 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1, CONF_WINDOW_AUTO_MAX_DURATION: 10, # Should be 0 for test @@ -1812,8 +1812,8 @@ async def test_window_action_frost_temp(hass: HomeAssistant, skip_hass_states_is 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_AUTO_OPEN_THRESHOLD: 0.1, CONF_WINDOW_AUTO_CLOSE_THRESHOLD: 0.1, CONF_WINDOW_AUTO_MAX_DURATION: 10, # Should be 0 for test @@ -2016,9 +2016,9 @@ async def test_bug_66( 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.5, - CONF_SECURITY_DEFAULT_ON_PERCENT: 0.1, # !! here + CONF_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.5, + CONF_SAFETY_DEFAULT_ON_PERCENT: 0.1, # !! here CONF_DEVICE_POWER: 200, CONF_WINDOW_SENSOR: "binary_sensor.mock_window_sensor", CONF_WINDOW_DELAY: 0, # important to not been obliged to wait @@ -2167,8 +2167,8 @@ async def test_window_action_frost_temp_preset_change( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_ACTION: CONF_WINDOW_FROST_TEMP, CONF_WINDOW_SENSOR: "binary_sensor.fake_window_sensor", CONF_WINDOW_DELAY: 1, @@ -2277,8 +2277,8 @@ async def test_window_action_frost_temp_temp_change( 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_SAFETY_DELAY_MIN: 5, + CONF_SAFETY_MIN_ON_PERCENT: 0.3, CONF_WINDOW_ACTION: CONF_WINDOW_FROST_TEMP, CONF_WINDOW_SENSOR: "binary_sensor.fake_window_sensor", CONF_WINDOW_DELAY: 1,