Compare commits
6 Commits
3.8.0.alph
...
3.8.0.beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
663a13809c | ||
|
|
3c497d24fb | ||
|
|
fe85ead916 | ||
|
|
7d5ced55d3 | ||
|
|
9d099e3169 | ||
|
|
49377de248 |
@@ -1219,7 +1219,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
await self.async_control_heating(force=True)
|
||||
|
||||
async def _async_internal_set_temperature(self, temperature):
|
||||
"""Set the target temperature and the target temperature of underlying climate if any"""
|
||||
"""Set the target temperature and the target temperature of underlying climate if any
|
||||
For testing purpose you can pass an event_timestamp.
|
||||
"""
|
||||
self._target_temp = temperature
|
||||
return
|
||||
|
||||
@@ -2247,7 +2249,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
await self.async_control_heating()
|
||||
self.update_custom_attributes()
|
||||
|
||||
#PR - Adding Window ByPass
|
||||
async def service_set_window_bypass_state(self, window_bypass):
|
||||
"""Called by a service call:
|
||||
service: versatile_thermostat.set_window_bypass
|
||||
|
||||
@@ -24,12 +24,12 @@ from .const import (
|
||||
SERVICE_SET_PRESENCE,
|
||||
SERVICE_SET_PRESET_TEMPERATURE,
|
||||
SERVICE_SET_SECURITY,
|
||||
#PR - Adding Window ByPass
|
||||
SERVICE_SET_WINDOW_BYPASS,
|
||||
SERVICE_SET_AUTO_REGULATION_MODE,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_SWITCH,
|
||||
CONF_THERMOSTAT_CLIMATE,
|
||||
CONF_THERMOSTAT_VALVE,
|
||||
CONF_THERMOSTAT_VALVE
|
||||
)
|
||||
|
||||
from .thermostat_switch import ThermostatOverSwitch
|
||||
@@ -99,7 +99,6 @@ async def async_setup_entry(
|
||||
"service_set_security",
|
||||
)
|
||||
|
||||
#PR - Adding Window ByPass
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_WINDOW_BYPASS,
|
||||
{
|
||||
@@ -108,3 +107,11 @@ async def async_setup_entry(
|
||||
},
|
||||
"service_set_window_bypass_state",
|
||||
)
|
||||
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_AUTO_REGULATION_MODE,
|
||||
{
|
||||
vol.Required("auto_regulation_mode"): vol.In(["None", "Light", "Medium", "Strong"]),
|
||||
},
|
||||
"service_set_auto_regulation_mode",
|
||||
)
|
||||
|
||||
@@ -1,18 +1,50 @@
|
||||
""" Some usefull commons class """
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from datetime import timedelta, datetime
|
||||
from homeassistant.core import HomeAssistant, callback, Event
|
||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||
from homeassistant.helpers.event import async_track_state_change_event, async_call_later
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .base_thermostat import BaseThermostat
|
||||
from .const import DOMAIN, DEVICE_MANUFACTURER
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def get_tz(hass: HomeAssistant):
|
||||
"""Get the current timezone"""
|
||||
|
||||
return dt_util.get_time_zone(hass.config.time_zone)
|
||||
|
||||
class NowClass:
|
||||
""" For testing purpose only"""
|
||||
|
||||
@staticmethod
|
||||
def get_now(hass: HomeAssistant) -> datetime:
|
||||
""" A test function to get the now.
|
||||
For testing purpose this method can be overriden to get a specific
|
||||
timestamp.
|
||||
"""
|
||||
return datetime.now( get_tz(hass))
|
||||
|
||||
def round_to_nearest(n:float, x: float)->float:
|
||||
""" Round a number to the nearest x (which should be decimal but not null)
|
||||
Example:
|
||||
nombre1 = 3.2
|
||||
nombre2 = 4.7
|
||||
x = 0.3
|
||||
|
||||
nombre_arrondi1 = round_to_nearest(nombre1, x)
|
||||
nombre_arrondi2 = round_to_nearest(nombre2, x)
|
||||
|
||||
print(nombre_arrondi1) # Output: 3.3
|
||||
print(nombre_arrondi2) # Output: 4.6
|
||||
"""
|
||||
assert x > 0
|
||||
return round(n * (1/x)) / (1/x)
|
||||
|
||||
class VersatileThermostatBaseEntity(Entity):
|
||||
"""A base class for all entities"""
|
||||
@@ -98,7 +130,7 @@ class VersatileThermostatBaseEntity(Entity):
|
||||
await try_find_climate(None)
|
||||
|
||||
@callback
|
||||
async def async_my_climate_changed(self, event: Event):
|
||||
async def async_my_climate_changed(self, event: Event): # pylint: disable=unused-argument
|
||||
"""Called when my climate have change
|
||||
This method aims to be overriden to take the status change
|
||||
"""
|
||||
|
||||
@@ -102,6 +102,8 @@ from .const import (
|
||||
CONF_AUTO_REGULATION_MODES,
|
||||
CONF_AUTO_REGULATION_MODE,
|
||||
CONF_AUTO_REGULATION_NONE,
|
||||
CONF_AUTO_REGULATION_DTEMP,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||
UnknownEntity,
|
||||
WindowOpenDetectionMethod,
|
||||
)
|
||||
@@ -264,6 +266,9 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
options=CONF_AUTO_REGULATION_MODES, translation_key="auto_regulation_mode"
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_AUTO_REGULATION_DTEMP, default=0.5): vol.Coerce(float),
|
||||
vol.Optional(CONF_AUTO_REGULATION_PERIOD_MIN, default=5): cv.positive_int
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ CONF_AUTO_REGULATION_NONE= "auto_regulation_none"
|
||||
CONF_AUTO_REGULATION_LIGHT= "auto_regulation_light"
|
||||
CONF_AUTO_REGULATION_MEDIUM= "auto_regulation_medium"
|
||||
CONF_AUTO_REGULATION_STRONG= "auto_regulation_strong"
|
||||
CONF_AUTO_REGULATION_DTEMP="auto_regulation_dtemp"
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN="auto_regulation_periode_min"
|
||||
|
||||
CONF_PRESETS = {
|
||||
p: f"{p}_temp"
|
||||
@@ -189,7 +191,9 @@ ALL_CONF = (
|
||||
CONF_VALVE_2,
|
||||
CONF_VALVE_3,
|
||||
CONF_VALVE_4,
|
||||
CONF_AUTO_REGULATION_MODE
|
||||
CONF_AUTO_REGULATION_MODE,
|
||||
CONF_AUTO_REGULATION_DTEMP,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN
|
||||
]
|
||||
+ CONF_PRESETS_VALUES
|
||||
+ CONF_PRESETS_AWAY_VALUES
|
||||
@@ -210,8 +214,8 @@ SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
SERVICE_SET_PRESENCE = "set_presence"
|
||||
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
||||
SERVICE_SET_SECURITY = "set_security"
|
||||
#PR - Adding Window ByPass
|
||||
SERVICE_SET_WINDOW_BYPASS = "set_window_bypass"
|
||||
SERVICE_SET_AUTO_REGULATION_MODE = "set_auto_regulation_mode"
|
||||
|
||||
DEFAULT_SECURITY_MIN_ON_PERCENT = 0.5
|
||||
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
|
||||
@@ -223,29 +227,40 @@ class RegulationParamLight:
|
||||
""" Light parameters for regulation"""
|
||||
kp:float = 0.2
|
||||
ki:float = 0.05
|
||||
k_ext:float = 0.05
|
||||
offset_max:float = 1.5
|
||||
stabilization_threshold:float = 0.1
|
||||
accumulated_error_threshold:float = 10
|
||||
|
||||
|
||||
class RegulationParamMedium:
|
||||
""" Light parameters for regulation"""
|
||||
kp:float = 0.3
|
||||
ki:float = 0.05
|
||||
k_ext:float = 0.1
|
||||
offset_max:float = 2
|
||||
stabilization_threshold:float = 0.1
|
||||
accumulated_error_threshold:float = 20
|
||||
|
||||
|
||||
class RegulationParamMedium:
|
||||
class RegulationParamStrong:
|
||||
""" Medium parameters for regulation"""
|
||||
kp:float = 0.5
|
||||
ki:float = 0.1
|
||||
kp:float = 0.4
|
||||
ki:float = 0.08
|
||||
k_ext:float = 0.1
|
||||
offset_max:float = 3
|
||||
stabilization_threshold:float = 0.1
|
||||
accumulated_error_threshold:float = 30
|
||||
accumulated_error_threshold:float = 25
|
||||
|
||||
class RegulationParamStrong:
|
||||
# Not used now
|
||||
class RegulationParamVeryStrong:
|
||||
""" Strong parameters for regulation"""
|
||||
kp:float = 0.6
|
||||
ki:float = 0.2
|
||||
ki:float = 0.1
|
||||
k_ext:float = 0.2
|
||||
offset_max:float = 4
|
||||
stabilization_threshold:float = 0.1
|
||||
accumulated_error_threshold:float = 40
|
||||
accumulated_error_threshold:float = 30
|
||||
|
||||
class EventType(Enum):
|
||||
"""The event type that can be sent"""
|
||||
|
||||
@@ -29,7 +29,8 @@ class PITemperatureRegulator:
|
||||
def set_target_temp(self, target_temp):
|
||||
""" Set the new target_temp"""
|
||||
self.target_temp = target_temp
|
||||
self.accumulated_error = 0
|
||||
# Do not reset the accumulated error
|
||||
# self.accumulated_error = 0
|
||||
|
||||
def calculate_regulated_temperature(self, internal_temp: float, external_temp:float): # pylint: disable=unused-argument
|
||||
""" Calculate a new target_temp given some temperature"""
|
||||
|
||||
@@ -137,4 +137,25 @@ set_window_bypass:
|
||||
advanced: false
|
||||
default: true
|
||||
selector:
|
||||
boolean:
|
||||
boolean:
|
||||
|
||||
set_auto_regulation_mode:
|
||||
name: Set Auto Regulation mode
|
||||
description: Change the mode of self-regulation (only for VTherm over climate)
|
||||
target:
|
||||
entity:
|
||||
integration: versatile_thermostat
|
||||
fields:
|
||||
auto_regulation_mode:
|
||||
name: Auto regulation mode
|
||||
description: Possible values
|
||||
required: true
|
||||
advanced: false
|
||||
default: true
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "None"
|
||||
- "Light"
|
||||
- "Medium"
|
||||
- "Strong"
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
"valve_entity2_id": "2nd valve number",
|
||||
"valve_entity3_id": "3rd valve number",
|
||||
"valve_entity4_id": "4th valve number",
|
||||
"auto_regulation_mode": "Self-regulation"
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Mandatory heater entity id",
|
||||
@@ -56,7 +58,9 @@
|
||||
"valve_entity2_id": "2nd valve number entity id",
|
||||
"valve_entity3_id": "3rd valve number entity id",
|
||||
"valve_entity4_id": "4th valve number entity id",
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature"
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature",
|
||||
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
|
||||
"auto_regulation_periode_min": "Duration in minutes between two regulation update"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -202,7 +206,9 @@
|
||||
"valve_entity2_id": "2nd valve number",
|
||||
"valve_entity3_id": "3rd valve number",
|
||||
"valve_entity4_id": "4th valve number",
|
||||
"auto_regulation_mode": "Self-regulation"
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Mandatory heater entity id",
|
||||
@@ -219,7 +225,9 @@
|
||||
"valve_entity2_id": "2nd valve number entity id",
|
||||
"valve_entity3_id": "3rd valve number entity id",
|
||||
"valve_entity4_id": "4th valve number entity id",
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature"
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature",
|
||||
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
|
||||
"auto_regulation_periode_min": "Duration in minutes between two regulation update"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# pylint: disable=line-too-long
|
||||
""" A climate over switch classe """
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.event import async_track_state_change_event, async_track_time_interval
|
||||
|
||||
from homeassistant.components.climate import HVACAction, HVACMode
|
||||
|
||||
from .commons import NowClass, round_to_nearest
|
||||
from .base_thermostat import BaseThermostat
|
||||
from .pi_algorithm import PITemperatureRegulator
|
||||
|
||||
@@ -22,6 +23,8 @@ from .const import (
|
||||
CONF_AUTO_REGULATION_LIGHT,
|
||||
CONF_AUTO_REGULATION_MEDIUM,
|
||||
CONF_AUTO_REGULATION_STRONG,
|
||||
CONF_AUTO_REGULATION_DTEMP,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||
RegulationParamLight,
|
||||
RegulationParamMedium,
|
||||
RegulationParamStrong
|
||||
@@ -33,9 +36,12 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class ThermostatOverClimate(BaseThermostat):
|
||||
"""Representation of a base class for a Versatile Thermostat over a climate"""
|
||||
_regulation_mode:str = None
|
||||
_auto_regulation_mode:str = None
|
||||
_regulation_algo = None
|
||||
_regulated_target_temp: float = None
|
||||
_auto_regulation_dtemp: float = None
|
||||
_auto_regulation_period_min: int = None
|
||||
_last_regulation_change: datetime = None
|
||||
|
||||
_entity_component_unrecorded_attributes = BaseThermostat._entity_component_unrecorded_attributes.union(frozenset(
|
||||
{
|
||||
@@ -48,6 +54,7 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
# super.__init__ calls post_init at the end. So it must be called after regulation initialization
|
||||
super().__init__(hass, unique_id, name, entry_infos)
|
||||
self._regulated_target_temp = self.target_temperature
|
||||
self._last_regulation_change = NowClass.get_now(hass)
|
||||
|
||||
@property
|
||||
def is_over_climate(self) -> bool:
|
||||
@@ -80,21 +87,37 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
await super()._async_internal_set_temperature(temperature)
|
||||
|
||||
self._regulation_algo.set_target_temp(self.target_temperature)
|
||||
await self._send_regulated_temperature()
|
||||
await self._send_regulated_temperature(force=True)
|
||||
|
||||
async def _send_regulated_temperature(self):
|
||||
async def _send_regulated_temperature(self, force=False):
|
||||
""" Sends the regulated temperature to all underlying """
|
||||
new_regulated_temp = self._regulation_algo.calculate_regulated_temperature(self.current_temperature, self._cur_ext_temp)
|
||||
if new_regulated_temp != self._regulated_target_temp:
|
||||
_LOGGER.info("%s - Regulated temp have changed to %.1f. Resend it to underlyings", self, new_regulated_temp)
|
||||
self._regulated_target_temp = new_regulated_temp
|
||||
if not self._regulated_target_temp:
|
||||
self._regulated_target_temp = self.target_temperature
|
||||
|
||||
for under in self._underlyings:
|
||||
await under.set_temperature(
|
||||
self.regulated_target_temp, self._attr_max_temp, self._attr_min_temp
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug("%s - No change on regulated temperature (%.1f)", self, self._regulated_target_temp)
|
||||
new_regulated_temp = round_to_nearest(
|
||||
self._regulation_algo.calculate_regulated_temperature(self.current_temperature, self._cur_ext_temp),
|
||||
self._auto_regulation_dtemp)
|
||||
dtemp = new_regulated_temp - self._regulated_target_temp
|
||||
|
||||
if not force and abs(dtemp) < self._auto_regulation_dtemp:
|
||||
_LOGGER.debug("%s - dtemp (%.1f) is < %.1f -> forget the regulation send", self, dtemp, self._auto_regulation_dtemp)
|
||||
return
|
||||
|
||||
now:datetime = NowClass.get_now(self._hass)
|
||||
period = float((now - self._last_regulation_change).total_seconds()) / 60.
|
||||
if not force and period < self._auto_regulation_period_min:
|
||||
_LOGGER.debug("%s - period (%.1f) is < %.0f -> forget the regulation send", self, period, self._auto_regulation_period_min)
|
||||
return
|
||||
|
||||
|
||||
self._regulated_target_temp = new_regulated_temp
|
||||
_LOGGER.info("%s - Regulated temp have changed to %.1f. Resend it to underlyings", self, new_regulated_temp)
|
||||
self._last_regulation_change = now
|
||||
|
||||
for under in self._underlyings:
|
||||
await under.set_temperature(
|
||||
self.regulated_target_temp, self._attr_max_temp, self._attr_min_temp
|
||||
)
|
||||
|
||||
@overrides
|
||||
def post_init(self, entry_infos):
|
||||
@@ -116,9 +139,17 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
)
|
||||
)
|
||||
|
||||
self._regulation_mode = entry_infos.get(CONF_AUTO_REGULATION_MODE) if entry_infos.get(CONF_AUTO_REGULATION_MODE) is not None else CONF_AUTO_REGULATION_NONE
|
||||
self.choose_auto_regulation_mode(
|
||||
entry_infos.get(CONF_AUTO_REGULATION_MODE) if entry_infos.get(CONF_AUTO_REGULATION_MODE) is not None else CONF_AUTO_REGULATION_NONE
|
||||
)
|
||||
|
||||
if self._regulation_mode == CONF_AUTO_REGULATION_LIGHT:
|
||||
self._auto_regulation_dtemp = entry_infos.get(CONF_AUTO_REGULATION_DTEMP) if entry_infos.get(CONF_AUTO_REGULATION_DTEMP) is not None else 0.5
|
||||
self._auto_regulation_period_min = entry_infos.get(CONF_AUTO_REGULATION_PERIOD_MIN) if entry_infos.get(CONF_AUTO_REGULATION_PERIOD_MIN) is not None else 5
|
||||
|
||||
def choose_auto_regulation_mode(self, auto_regulation_mode):
|
||||
""" Choose or change the regulation mode"""
|
||||
self._auto_regulation_mode = auto_regulation_mode
|
||||
if self._auto_regulation_mode == CONF_AUTO_REGULATION_LIGHT:
|
||||
self._regulation_algo = PITemperatureRegulator(
|
||||
self.target_temperature,
|
||||
RegulationParamLight.kp,
|
||||
@@ -127,7 +158,7 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
RegulationParamLight.offset_max,
|
||||
RegulationParamLight.stabilization_threshold,
|
||||
RegulationParamLight.accumulated_error_threshold)
|
||||
elif self._regulation_mode == CONF_AUTO_REGULATION_MEDIUM:
|
||||
elif self._auto_regulation_mode == CONF_AUTO_REGULATION_MEDIUM:
|
||||
self._regulation_algo = PITemperatureRegulator(
|
||||
self.target_temperature,
|
||||
RegulationParamMedium.kp,
|
||||
@@ -136,7 +167,7 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
RegulationParamMedium.offset_max,
|
||||
RegulationParamMedium.stabilization_threshold,
|
||||
RegulationParamMedium.accumulated_error_threshold)
|
||||
elif self._regulation_mode == CONF_AUTO_REGULATION_STRONG:
|
||||
elif self._auto_regulation_mode == CONF_AUTO_REGULATION_STRONG:
|
||||
self._regulation_algo = PITemperatureRegulator(
|
||||
self.target_temperature,
|
||||
RegulationParamStrong.kp,
|
||||
@@ -430,9 +461,9 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
return ret
|
||||
|
||||
@property
|
||||
def regulation_mode(self):
|
||||
def auto_regulation_mode(self):
|
||||
""" Get the regulation mode """
|
||||
return self._regulation_mode
|
||||
return self._auto_regulation_mode
|
||||
|
||||
@property
|
||||
def regulated_target_temp(self):
|
||||
@@ -442,7 +473,7 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
@property
|
||||
def is_regulated(self):
|
||||
""" Check if the ThermostatOverClimate is regulated """
|
||||
return self.regulation_mode != CONF_AUTO_REGULATION_NONE
|
||||
return self.auto_regulation_mode != CONF_AUTO_REGULATION_NONE
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
@@ -617,3 +648,24 @@ class ThermostatOverClimate(BaseThermostat):
|
||||
await under.set_swing_mode(swing_mode)
|
||||
self._swing_mode = swing_mode
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def service_set_auto_regulation_mode(self, auto_regulation_mode):
|
||||
"""Called by a service call:
|
||||
service: versatile_thermostat.set_auto_regulation_mode
|
||||
data:
|
||||
auto_regulation_mode: [None | Light | Medium | Strong]
|
||||
target:
|
||||
entity_id: climate.thermostat_1
|
||||
"""
|
||||
_LOGGER.info("%s - Calling service_set_auto_regulation_mode, auto_regulation_mode: %s", self, auto_regulation_mode)
|
||||
if auto_regulation_mode == "None":
|
||||
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_NONE)
|
||||
elif auto_regulation_mode == "Light":
|
||||
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_LIGHT)
|
||||
elif auto_regulation_mode == "Medium":
|
||||
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_MEDIUM)
|
||||
elif auto_regulation_mode == "Strong":
|
||||
self.choose_auto_regulation_mode(CONF_AUTO_REGULATION_STRONG)
|
||||
|
||||
await self._send_regulated_temperature()
|
||||
self.update_custom_attributes()
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
"valve_entity2_id": "2nd valve number",
|
||||
"valve_entity3_id": "3rd valve number",
|
||||
"valve_entity4_id": "4th valve number",
|
||||
"auto_regulation_mode": "Self-regulation"
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Mandatory heater entity id",
|
||||
@@ -56,7 +58,9 @@
|
||||
"valve_entity2_id": "2nd valve number entity id",
|
||||
"valve_entity3_id": "3rd valve number entity id",
|
||||
"valve_entity4_id": "4th valve number entity id",
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature"
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature",
|
||||
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
|
||||
"auto_regulation_periode_min": "Duration in minutes between two regulation update"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -202,7 +206,9 @@
|
||||
"valve_entity2_id": "2nd valve number",
|
||||
"valve_entity3_id": "3rd valve number",
|
||||
"valve_entity4_id": "4th valve number",
|
||||
"auto_regulation_mode": "Self-regulation"
|
||||
"auto_regulation_mode": "Self-regulation",
|
||||
"auto_regulation_dtemp": "Regulation threshold",
|
||||
"auto_regulation_periode_min": "Regulation minimal period"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Mandatory heater entity id",
|
||||
@@ -219,7 +225,9 @@
|
||||
"valve_entity2_id": "2nd valve number entity id",
|
||||
"valve_entity3_id": "3rd valve number entity id",
|
||||
"valve_entity4_id": "4th valve number entity id",
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature"
|
||||
"auto_regulation_mode": "Auto adjustment of the target temperature",
|
||||
"auto_regulation_dtemp": "The threshold in ° under which the temperature change will not be send",
|
||||
"auto_regulation_periode_min": "Duration in minutes between two regulation update"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
"valve_entity2_id": "2ème valve number",
|
||||
"valve_entity3_id": "3ème valve number",
|
||||
"valve_entity4_id": "4ème valve number",
|
||||
"auto_regulation_mode": "Auto-régulation"
|
||||
"auto_regulation_mode": "Auto-régulation",
|
||||
"auto_regulation_dtemp": "Seuil de régulation",
|
||||
"auto_regulation_periode_min": "Période minimale de régulation"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
||||
@@ -56,7 +58,9 @@
|
||||
"valve_entity2_id": "Entity id de la 2ème valve",
|
||||
"valve_entity3_id": "Entity id de la 3ème valve",
|
||||
"valve_entity4_id": "Entity id de la 4ème valve",
|
||||
"auto_regulation_mode": "Ajustement automatique de la température cible"
|
||||
"auto_regulation_mode": "Ajustement automatique de la température cible",
|
||||
"auto_regulation_dtemp": "Le seuil en ° au-dessous duquel la régulation ne sera pas envoyée",
|
||||
"auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
@@ -203,7 +207,9 @@
|
||||
"valve_entity2_id": "2ème valve",
|
||||
"valve_entity3_id": "3ème valve",
|
||||
"valve_entity4_id": "4ème valve",
|
||||
"auto_regulation_mode": "Auto-regulation"
|
||||
"auto_regulation_mode": "Auto-regulation",
|
||||
"auto_regulation_dtemp": "Seuil de régulation",
|
||||
"auto_regulation_periode_min": "Période minimale de régulation"
|
||||
},
|
||||
"data_description": {
|
||||
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
||||
@@ -220,7 +226,9 @@
|
||||
"valve_entity2_id": "Entity id de la 2ème valve",
|
||||
"valve_entity3_id": "Entity id de la 3ème valve",
|
||||
"valve_entity4_id": "Entity id de la 4ème valve",
|
||||
"auto_regulation_mode": "Ajustement automatique de la consigne"
|
||||
"auto_regulation_mode": "Ajustement automatique de la consigne",
|
||||
"auto_regulation_dtemp": "Le seuil en ° au-dessous duquel la régulation ne sera pas envoyée",
|
||||
"auto_regulation_periode_min": "La durée en minutes entre deux mise à jour faites par la régulation"
|
||||
}
|
||||
},
|
||||
"tpi": {
|
||||
|
||||
@@ -10,7 +10,6 @@ from homeassistant.core import HomeAssistant, Event, EVENT_STATE_CHANGED, State
|
||||
from homeassistant.const import UnitOfTemperature, STATE_ON, STATE_OFF, ATTR_TEMPERATURE
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.climate import (
|
||||
ClimateEntity,
|
||||
@@ -25,6 +24,7 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry
|
||||
from custom_components.versatile_thermostat.base_thermostat import BaseThermostat
|
||||
from custom_components.versatile_thermostat.const import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
from custom_components.versatile_thermostat.underlyings import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
from custom_components.versatile_thermostat.commons import get_tz, NowClass # pylint: disable=unused-import
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
||||
@@ -478,13 +478,6 @@ async def send_presence_change_event(
|
||||
await asyncio.sleep(0.1)
|
||||
return ret
|
||||
|
||||
|
||||
def get_tz(hass: HomeAssistant):
|
||||
"""Get the current timezone"""
|
||||
|
||||
return dt_util.get_time_zone(hass.config.time_zone)
|
||||
|
||||
|
||||
async def send_climate_change_event(
|
||||
entity: BaseThermostat,
|
||||
new_hvac_mode: HVACMode,
|
||||
|
||||
@@ -51,8 +51,10 @@ from custom_components.versatile_thermostat.const import (
|
||||
PRESET_AWAY_SUFFIX,
|
||||
CONF_CLIMATE,
|
||||
CONF_AUTO_REGULATION_MODE,
|
||||
CONF_AUTO_REGULATION_MEDIUM,
|
||||
CONF_AUTO_REGULATION_STRONG,
|
||||
CONF_AUTO_REGULATION_NONE,
|
||||
CONF_AUTO_REGULATION_DTEMP,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN
|
||||
)
|
||||
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
||||
CONF_NAME: "TheOverSwitchMockName",
|
||||
@@ -125,7 +127,9 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
||||
CONF_CLIMATE: "climate.mock_climate",
|
||||
CONF_AC_MODE: False,
|
||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM
|
||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
|
||||
CONF_AUTO_REGULATION_DTEMP: 0.5,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN: 2
|
||||
}
|
||||
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = {
|
||||
@@ -137,7 +141,9 @@ MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = {
|
||||
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
|
||||
CONF_CLIMATE: "climate.mock_climate",
|
||||
CONF_AC_MODE: True,
|
||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM
|
||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_STRONG,
|
||||
CONF_AUTO_REGULATION_DTEMP: 0.5,
|
||||
CONF_AUTO_REGULATION_PERIOD_MIN: 1
|
||||
}
|
||||
|
||||
MOCK_PRESETS_CONFIG = {
|
||||
|
||||
@@ -20,7 +20,7 @@ from .commons import * # pylint: disable=wildcard-import, unused-wildcard-impor
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
|
||||
"""Test the regulation of an over climate thermostat"""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
@@ -37,8 +37,11 @@ async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_
|
||||
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||
|
||||
# Creates the regulated VTherm over climate
|
||||
# change temperature so that the heating will start
|
||||
event_timestamp = now - timedelta(minutes=10)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||
return_value=fake_underlying_climate,
|
||||
@@ -73,45 +76,50 @@ async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_
|
||||
]
|
||||
assert entity.preset_mode is PRESET_NONE
|
||||
|
||||
# Activate the heating by changing HVACMode and temperature
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
):
|
||||
# Activate the heating by changing HVACMode and temperature
|
||||
# Select a hvacmode, presence and preset
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.hvac_action == HVACAction.OFF
|
||||
|
||||
# change temperature so that the heating will start
|
||||
event_timestamp = now - timedelta(minutes=10)
|
||||
assert entity.regulated_target_temp == entity.min_temp
|
||||
|
||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 10, event_timestamp)
|
||||
|
||||
# set manual target temp (at now - 7) -> the regulation should occurs
|
||||
event_timestamp = now - timedelta(minutes=7)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await entity.async_set_temperature(temperature=18)
|
||||
|
||||
# set manual target temp
|
||||
await entity.async_set_temperature(temperature=18)
|
||||
fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
||||
|
||||
fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
||||
|
||||
# the regulated temperature should be greater
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
assert entity.regulated_target_temp == 18+2.9 # In medium we could go up to +3 degre
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
# the regulated temperature should be greater
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
# In medium we could go up to +3 degre
|
||||
# normally the calcul gives 18 + 2.2 but we round the result to the nearest 0.5 which is 2.0
|
||||
assert entity.regulated_target_temp == 18+2.0
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
|
||||
# change temperature so that the regulated temperature should slow down
|
||||
event_timestamp = now - timedelta(minutes=9)
|
||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 18, event_timestamp)
|
||||
event_timestamp = now - timedelta(minutes=5)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await send_temperature_change_event(entity, 22, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 19, event_timestamp)
|
||||
|
||||
# the regulated temperature should be under
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 18-0.1
|
||||
# the regulated temperature should be under
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 18-0.5 # normally 0.6 but round_to_nearest gives 0.5
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_states_is_state):
|
||||
async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
|
||||
"""Test the regulation of an over climate thermostat"""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
@@ -128,8 +136,11 @@ async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_st
|
||||
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||
|
||||
# Creates the regulated VTherm over climate
|
||||
# change temperature so that the heating will start
|
||||
event_timestamp = now - timedelta(minutes=10)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||
return_value=fake_underlying_climate,
|
||||
@@ -164,47 +175,160 @@ async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_st
|
||||
]
|
||||
assert entity.preset_mode is PRESET_NONE
|
||||
|
||||
# Activate the heating by changing HVACMode and temperature
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
||||
):
|
||||
# Activate the heating by changing HVACMode and temperature
|
||||
# Select a hvacmode, presence and preset
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
assert entity.hvac_action == HVACAction.OFF
|
||||
|
||||
# change temperature so that the heating will start
|
||||
event_timestamp = now - timedelta(minutes=10)
|
||||
await send_temperature_change_event(entity, 30, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
||||
|
||||
|
||||
# set manual target temp
|
||||
await entity.async_set_temperature(temperature=25)
|
||||
event_timestamp = now - timedelta(minutes=7)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await entity.async_set_temperature(temperature=25)
|
||||
|
||||
fake_underlying_climate.set_hvac_action(HVACAction.COOLING) # simulate under heating
|
||||
assert entity.hvac_action == HVACAction.COOLING
|
||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
||||
fake_underlying_climate.set_hvac_action(HVACAction.COOLING) # simulate under heating
|
||||
assert entity.hvac_action == HVACAction.COOLING
|
||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
||||
|
||||
# the regulated temperature should be lower
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25-3 # In medium we could go up to -3 degre
|
||||
assert entity.hvac_action == HVACAction.COOLING
|
||||
# the regulated temperature should be lower
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25-3 # In medium we could go up to -3 degre
|
||||
assert entity.hvac_action == HVACAction.COOLING
|
||||
|
||||
# change temperature so that the regulated temperature should slow down
|
||||
event_timestamp = now - timedelta(minutes=9)
|
||||
await send_temperature_change_event(entity, 26, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
||||
event_timestamp = now - timedelta(minutes=5)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await send_temperature_change_event(entity, 26, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
||||
|
||||
# the regulated temperature should be under
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25-2.7
|
||||
# the regulated temperature should be under
|
||||
assert entity.regulated_target_temp < entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25-2.5 # +2.3 without round_to_nearest
|
||||
|
||||
# change temperature so that the regulated temperature should slow down
|
||||
event_timestamp = now - timedelta(minutes=9)
|
||||
await send_temperature_change_event(entity, 20, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 30, event_timestamp)
|
||||
# change temperature so that the regulated temperature should slow down
|
||||
event_timestamp = now - timedelta(minutes=3)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await send_temperature_change_event(entity, 20, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 25, event_timestamp)
|
||||
|
||||
# the regulated temperature should be greater
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25+1.8
|
||||
# the regulated temperature should be greater
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
assert entity.regulated_target_temp == 25+0.5 # +0.4 without round_to_nearest
|
||||
|
||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
@pytest.mark.parametrize("expected_lingering_timers", [True])
|
||||
async def test_over_climate_regulation_limitations(hass: HomeAssistant, skip_hass_states_is_state, skip_send_event):
|
||||
"""Test the limitations of the regulation of an over climate thermostat:
|
||||
1. test the period_min parameter: do not send regulation event too frequently
|
||||
2. test the dtemp parameter: do not send regulation event if offset temp is lower than dtemp
|
||||
"""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="TheOverClimateMockName",
|
||||
unique_id="uniqueId",
|
||||
# This is include a medium regulation, dtemp=0.5, period_min=2
|
||||
data=PARTIAL_CLIMATE_CONFIG,
|
||||
)
|
||||
|
||||
tz = get_tz(hass) # pylint: disable=invalid-name
|
||||
now: datetime = datetime.now(tz=tz)
|
||||
|
||||
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||
|
||||
# Creates the regulated VTherm over climate at t-20
|
||||
# change temperature so that the heating will start
|
||||
event_timestamp = now - timedelta(minutes=20)
|
||||
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
), patch(
|
||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||
return_value=fake_underlying_climate,
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
def find_my_entity(entity_id) -> ClimateEntity:
|
||||
"""Find my new entity"""
|
||||
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
|
||||
for entity in component.entities:
|
||||
if entity.entity_id == entity_id:
|
||||
return entity
|
||||
|
||||
entity:ThermostatOverClimate = find_my_entity("climate.theoverclimatemockname")
|
||||
|
||||
assert entity
|
||||
assert isinstance(entity, ThermostatOverClimate)
|
||||
|
||||
assert entity.name == "TheOverClimateMockName"
|
||||
assert entity.is_over_climate is True
|
||||
assert entity.is_regulated is True
|
||||
|
||||
# Activate the heating by changing HVACMode and temperature
|
||||
# Select a hvacmode, presence and preset
|
||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||
assert entity.hvac_mode is HVACMode.HEAT
|
||||
|
||||
# it is cold today
|
||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 10, event_timestamp)
|
||||
|
||||
# set manual target temp (at now - 19) -> the regulation should be ignored because too early
|
||||
event_timestamp = now - timedelta(minutes=19)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await entity.async_set_temperature(temperature=18)
|
||||
|
||||
fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
|
||||
assert entity.hvac_action == HVACAction.HEATING
|
||||
|
||||
# the regulated temperature will change because when we set temp manually it is forced
|
||||
assert entity.regulated_target_temp == 20.
|
||||
|
||||
# set manual target temp (at now - 18) -> the regulation should be taken into account
|
||||
event_timestamp = now - timedelta(minutes=18)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await entity.async_set_temperature(temperature=17)
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
assert entity.regulated_target_temp == 18+1 # In strong we could go up to +3 degre. 0.7 without round_to_nearest
|
||||
old_regulated_temp = entity.regulated_target_temp
|
||||
|
||||
# change temperature so that dtemp < 0.5 and time is > period_min (+ 3min)
|
||||
event_timestamp = now - timedelta(minutes=15)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await send_temperature_change_event(entity, 16, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 10, event_timestamp)
|
||||
|
||||
# the regulated temperature should be under
|
||||
assert entity.regulated_target_temp == old_regulated_temp
|
||||
|
||||
# change temperature so that dtemp > 0.5 and time is > period_min (+ 3min)
|
||||
event_timestamp = now - timedelta(minutes=12)
|
||||
with patch(
|
||||
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||
):
|
||||
await send_temperature_change_event(entity, 18, event_timestamp)
|
||||
await send_ext_temperature_change_event(entity, 12, event_timestamp)
|
||||
|
||||
# the regulated should have been done
|
||||
assert entity.regulated_target_temp != old_regulated_temp
|
||||
assert entity.regulated_target_temp > entity.target_temperature
|
||||
assert entity.regulated_target_temp == 17 + 1 # 0.7 without round_to_nearest
|
||||
Reference in New Issue
Block a user