Add regulation limitations
This commit is contained in:
@@ -1219,7 +1219,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self.async_control_heating(force=True)
|
await self.async_control_heating(force=True)
|
||||||
|
|
||||||
async def _async_internal_set_temperature(self, temperature):
|
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
|
self._target_temp = temperature
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2247,7 +2249,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
|||||||
await self.async_control_heating()
|
await self.async_control_heating()
|
||||||
self.update_custom_attributes()
|
self.update_custom_attributes()
|
||||||
|
|
||||||
#PR - Adding Window ByPass
|
|
||||||
async def service_set_window_bypass_state(self, window_bypass):
|
async def service_set_window_bypass_state(self, window_bypass):
|
||||||
"""Called by a service call:
|
"""Called by a service call:
|
||||||
service: versatile_thermostat.set_window_bypass
|
service: versatile_thermostat.set_window_bypass
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ from .const import (
|
|||||||
SERVICE_SET_PRESENCE,
|
SERVICE_SET_PRESENCE,
|
||||||
SERVICE_SET_PRESET_TEMPERATURE,
|
SERVICE_SET_PRESET_TEMPERATURE,
|
||||||
SERVICE_SET_SECURITY,
|
SERVICE_SET_SECURITY,
|
||||||
#PR - Adding Window ByPass
|
|
||||||
SERVICE_SET_WINDOW_BYPASS,
|
SERVICE_SET_WINDOW_BYPASS,
|
||||||
|
SERVICE_SET_AUTO_REGULATION_MODE,
|
||||||
CONF_THERMOSTAT_TYPE,
|
CONF_THERMOSTAT_TYPE,
|
||||||
CONF_THERMOSTAT_SWITCH,
|
CONF_THERMOSTAT_SWITCH,
|
||||||
CONF_THERMOSTAT_CLIMATE,
|
CONF_THERMOSTAT_CLIMATE,
|
||||||
CONF_THERMOSTAT_VALVE,
|
CONF_THERMOSTAT_VALVE
|
||||||
)
|
)
|
||||||
|
|
||||||
from .thermostat_switch import ThermostatOverSwitch
|
from .thermostat_switch import ThermostatOverSwitch
|
||||||
@@ -99,7 +99,6 @@ async def async_setup_entry(
|
|||||||
"service_set_security",
|
"service_set_security",
|
||||||
)
|
)
|
||||||
|
|
||||||
#PR - Adding Window ByPass
|
|
||||||
platform.async_register_entity_service(
|
platform.async_register_entity_service(
|
||||||
SERVICE_SET_WINDOW_BYPASS,
|
SERVICE_SET_WINDOW_BYPASS,
|
||||||
{
|
{
|
||||||
@@ -108,3 +107,11 @@ async def async_setup_entry(
|
|||||||
},
|
},
|
||||||
"service_set_window_bypass_state",
|
"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,34 @@
|
|||||||
""" Some usefull commons class """
|
""" Some usefull commons class """
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta, datetime
|
||||||
from homeassistant.core import HomeAssistant, callback, Event
|
from homeassistant.core import HomeAssistant, callback, Event
|
||||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||||
from homeassistant.helpers.event import async_track_state_change_event, async_call_later
|
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 .base_thermostat import BaseThermostat
|
||||||
from .const import DOMAIN, DEVICE_MANUFACTURER
|
from .const import DOMAIN, DEVICE_MANUFACTURER
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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))
|
||||||
|
|
||||||
class VersatileThermostatBaseEntity(Entity):
|
class VersatileThermostatBaseEntity(Entity):
|
||||||
"""A base class for all entities"""
|
"""A base class for all entities"""
|
||||||
@@ -98,7 +114,7 @@ class VersatileThermostatBaseEntity(Entity):
|
|||||||
await try_find_climate(None)
|
await try_find_climate(None)
|
||||||
|
|
||||||
@callback
|
@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
|
"""Called when my climate have change
|
||||||
This method aims to be overriden to take the status 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_MODES,
|
||||||
CONF_AUTO_REGULATION_MODE,
|
CONF_AUTO_REGULATION_MODE,
|
||||||
CONF_AUTO_REGULATION_NONE,
|
CONF_AUTO_REGULATION_NONE,
|
||||||
|
CONF_AUTO_REGULATION_DTEMP,
|
||||||
|
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||||
UnknownEntity,
|
UnknownEntity,
|
||||||
WindowOpenDetectionMethod,
|
WindowOpenDetectionMethod,
|
||||||
)
|
)
|
||||||
@@ -264,6 +266,9 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
|||||||
options=CONF_AUTO_REGULATION_MODES, translation_key="auto_regulation_mode"
|
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_LIGHT= "auto_regulation_light"
|
||||||
CONF_AUTO_REGULATION_MEDIUM= "auto_regulation_medium"
|
CONF_AUTO_REGULATION_MEDIUM= "auto_regulation_medium"
|
||||||
CONF_AUTO_REGULATION_STRONG= "auto_regulation_strong"
|
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 = {
|
CONF_PRESETS = {
|
||||||
p: f"{p}_temp"
|
p: f"{p}_temp"
|
||||||
@@ -189,7 +191,9 @@ ALL_CONF = (
|
|||||||
CONF_VALVE_2,
|
CONF_VALVE_2,
|
||||||
CONF_VALVE_3,
|
CONF_VALVE_3,
|
||||||
CONF_VALVE_4,
|
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_VALUES
|
||||||
+ CONF_PRESETS_AWAY_VALUES
|
+ CONF_PRESETS_AWAY_VALUES
|
||||||
@@ -210,8 +214,8 @@ SUPPORT_FLAGS = ClimateEntityFeature.TARGET_TEMPERATURE
|
|||||||
SERVICE_SET_PRESENCE = "set_presence"
|
SERVICE_SET_PRESENCE = "set_presence"
|
||||||
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature"
|
||||||
SERVICE_SET_SECURITY = "set_security"
|
SERVICE_SET_SECURITY = "set_security"
|
||||||
#PR - Adding Window ByPass
|
|
||||||
SERVICE_SET_WINDOW_BYPASS = "set_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_MIN_ON_PERCENT = 0.5
|
||||||
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
|
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
|
||||||
|
|||||||
@@ -137,4 +137,25 @@ set_window_bypass:
|
|||||||
advanced: false
|
advanced: false
|
||||||
default: true
|
default: true
|
||||||
selector:
|
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_entity2_id": "2nd valve number",
|
||||||
"valve_entity3_id": "3rd valve number",
|
"valve_entity3_id": "3rd valve number",
|
||||||
"valve_entity4_id": "4th 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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Mandatory heater entity id",
|
"heater_entity_id": "Mandatory heater entity id",
|
||||||
@@ -56,7 +58,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number entity id",
|
"valve_entity2_id": "2nd valve number entity id",
|
||||||
"valve_entity3_id": "3rd valve number entity id",
|
"valve_entity3_id": "3rd valve number entity id",
|
||||||
"valve_entity4_id": "4th 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": {
|
"tpi": {
|
||||||
@@ -202,7 +206,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number",
|
"valve_entity2_id": "2nd valve number",
|
||||||
"valve_entity3_id": "3rd valve number",
|
"valve_entity3_id": "3rd valve number",
|
||||||
"valve_entity4_id": "4th 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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Mandatory heater entity id",
|
"heater_entity_id": "Mandatory heater entity id",
|
||||||
@@ -219,7 +225,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number entity id",
|
"valve_entity2_id": "2nd valve number entity id",
|
||||||
"valve_entity3_id": "3rd valve number entity id",
|
"valve_entity3_id": "3rd valve number entity id",
|
||||||
"valve_entity4_id": "4th 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": {
|
"tpi": {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
# pylint: disable=line-too-long
|
# pylint: disable=line-too-long
|
||||||
""" A climate over switch classe """
|
""" A climate over switch classe """
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta, datetime
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.event import async_track_state_change_event, async_track_time_interval
|
from homeassistant.helpers.event import async_track_state_change_event, async_track_time_interval
|
||||||
|
|
||||||
from homeassistant.components.climate import HVACAction, HVACMode
|
from homeassistant.components.climate import HVACAction, HVACMode
|
||||||
|
|
||||||
|
from .commons import NowClass
|
||||||
from .base_thermostat import BaseThermostat
|
from .base_thermostat import BaseThermostat
|
||||||
from .pi_algorithm import PITemperatureRegulator
|
from .pi_algorithm import PITemperatureRegulator
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@ from .const import (
|
|||||||
CONF_AUTO_REGULATION_LIGHT,
|
CONF_AUTO_REGULATION_LIGHT,
|
||||||
CONF_AUTO_REGULATION_MEDIUM,
|
CONF_AUTO_REGULATION_MEDIUM,
|
||||||
CONF_AUTO_REGULATION_STRONG,
|
CONF_AUTO_REGULATION_STRONG,
|
||||||
|
CONF_AUTO_REGULATION_DTEMP,
|
||||||
|
CONF_AUTO_REGULATION_PERIOD_MIN,
|
||||||
RegulationParamLight,
|
RegulationParamLight,
|
||||||
RegulationParamMedium,
|
RegulationParamMedium,
|
||||||
RegulationParamStrong
|
RegulationParamStrong
|
||||||
@@ -33,9 +36,12 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class ThermostatOverClimate(BaseThermostat):
|
class ThermostatOverClimate(BaseThermostat):
|
||||||
"""Representation of a base class for a Versatile Thermostat over a climate"""
|
"""Representation of a base class for a Versatile Thermostat over a climate"""
|
||||||
_regulation_mode:str = None
|
_auto_regulation_mode:str = None
|
||||||
_regulation_algo = None
|
_regulation_algo = None
|
||||||
_regulated_target_temp: float = 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(
|
_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__ calls post_init at the end. So it must be called after regulation initialization
|
||||||
super().__init__(hass, unique_id, name, entry_infos)
|
super().__init__(hass, unique_id, name, entry_infos)
|
||||||
self._regulated_target_temp = self.target_temperature
|
self._regulated_target_temp = self.target_temperature
|
||||||
|
self._last_regulation_change = NowClass.get_now(hass)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_over_climate(self) -> bool:
|
def is_over_climate(self) -> bool:
|
||||||
@@ -84,17 +91,31 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
|
|
||||||
async def _send_regulated_temperature(self):
|
async def _send_regulated_temperature(self):
|
||||||
""" Sends the regulated temperature to all underlying """
|
""" Sends the regulated temperature to all underlying """
|
||||||
new_regulated_temp = self._regulation_algo.calculate_regulated_temperature(self.current_temperature, self._cur_ext_temp)
|
if not self._regulated_target_temp:
|
||||||
if new_regulated_temp != self._regulated_target_temp:
|
self._regulated_target_temp = self.target_temperature
|
||||||
_LOGGER.info("%s - Regulated temp have changed to %.1f. Resend it to underlyings", self, new_regulated_temp)
|
|
||||||
self._regulated_target_temp = new_regulated_temp
|
|
||||||
|
|
||||||
for under in self._underlyings:
|
new_regulated_temp = self._regulation_algo.calculate_regulated_temperature(self.current_temperature, self._cur_ext_temp)
|
||||||
await under.set_temperature(
|
dtemp = new_regulated_temp - self._regulated_target_temp
|
||||||
self.regulated_target_temp, self._attr_max_temp, self._attr_min_temp
|
|
||||||
)
|
if abs(dtemp) < self._auto_regulation_dtemp:
|
||||||
else:
|
_LOGGER.info("!!!!! %s - dtemp (%.1f) is < %.1f -> forget the regulation send", self, dtemp, self._auto_regulation_dtemp)
|
||||||
_LOGGER.debug("%s - No change on regulated temperature (%.1f)", self, self._regulated_target_temp)
|
return
|
||||||
|
|
||||||
|
now:datetime = NowClass.get_now(self._hass)
|
||||||
|
period = float((now - self._last_regulation_change).total_seconds()) / 60.
|
||||||
|
if 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
|
@overrides
|
||||||
def post_init(self, entry_infos):
|
def post_init(self, entry_infos):
|
||||||
@@ -116,9 +137,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._regulation_algo = PITemperatureRegulator(
|
||||||
self.target_temperature,
|
self.target_temperature,
|
||||||
RegulationParamLight.kp,
|
RegulationParamLight.kp,
|
||||||
@@ -127,7 +156,7 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
RegulationParamLight.offset_max,
|
RegulationParamLight.offset_max,
|
||||||
RegulationParamLight.stabilization_threshold,
|
RegulationParamLight.stabilization_threshold,
|
||||||
RegulationParamLight.accumulated_error_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._regulation_algo = PITemperatureRegulator(
|
||||||
self.target_temperature,
|
self.target_temperature,
|
||||||
RegulationParamMedium.kp,
|
RegulationParamMedium.kp,
|
||||||
@@ -136,7 +165,7 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
RegulationParamMedium.offset_max,
|
RegulationParamMedium.offset_max,
|
||||||
RegulationParamMedium.stabilization_threshold,
|
RegulationParamMedium.stabilization_threshold,
|
||||||
RegulationParamMedium.accumulated_error_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._regulation_algo = PITemperatureRegulator(
|
||||||
self.target_temperature,
|
self.target_temperature,
|
||||||
RegulationParamStrong.kp,
|
RegulationParamStrong.kp,
|
||||||
@@ -430,9 +459,9 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def regulation_mode(self):
|
def auto_regulation_mode(self):
|
||||||
""" Get the regulation mode """
|
""" Get the regulation mode """
|
||||||
return self._regulation_mode
|
return self._auto_regulation_mode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def regulated_target_temp(self):
|
def regulated_target_temp(self):
|
||||||
@@ -442,7 +471,7 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
@property
|
@property
|
||||||
def is_regulated(self):
|
def is_regulated(self):
|
||||||
""" Check if the ThermostatOverClimate is regulated """
|
""" Check if the ThermostatOverClimate is regulated """
|
||||||
return self.regulation_mode != CONF_AUTO_REGULATION_NONE
|
return self.auto_regulation_mode != CONF_AUTO_REGULATION_NONE
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hvac_modes(self):
|
def hvac_modes(self):
|
||||||
@@ -617,3 +646,24 @@ class ThermostatOverClimate(BaseThermostat):
|
|||||||
await under.set_swing_mode(swing_mode)
|
await under.set_swing_mode(swing_mode)
|
||||||
self._swing_mode = swing_mode
|
self._swing_mode = swing_mode
|
||||||
self.async_write_ha_state()
|
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_entity2_id": "2nd valve number",
|
||||||
"valve_entity3_id": "3rd valve number",
|
"valve_entity3_id": "3rd valve number",
|
||||||
"valve_entity4_id": "4th 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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Mandatory heater entity id",
|
"heater_entity_id": "Mandatory heater entity id",
|
||||||
@@ -56,7 +58,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number entity id",
|
"valve_entity2_id": "2nd valve number entity id",
|
||||||
"valve_entity3_id": "3rd valve number entity id",
|
"valve_entity3_id": "3rd valve number entity id",
|
||||||
"valve_entity4_id": "4th 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": {
|
"tpi": {
|
||||||
@@ -202,7 +206,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number",
|
"valve_entity2_id": "2nd valve number",
|
||||||
"valve_entity3_id": "3rd valve number",
|
"valve_entity3_id": "3rd valve number",
|
||||||
"valve_entity4_id": "4th 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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Mandatory heater entity id",
|
"heater_entity_id": "Mandatory heater entity id",
|
||||||
@@ -219,7 +225,9 @@
|
|||||||
"valve_entity2_id": "2nd valve number entity id",
|
"valve_entity2_id": "2nd valve number entity id",
|
||||||
"valve_entity3_id": "3rd valve number entity id",
|
"valve_entity3_id": "3rd valve number entity id",
|
||||||
"valve_entity4_id": "4th 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": {
|
"tpi": {
|
||||||
|
|||||||
@@ -39,7 +39,9 @@
|
|||||||
"valve_entity2_id": "2ème valve number",
|
"valve_entity2_id": "2ème valve number",
|
||||||
"valve_entity3_id": "3ème valve number",
|
"valve_entity3_id": "3ème valve number",
|
||||||
"valve_entity4_id": "4è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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
||||||
@@ -56,7 +58,9 @@
|
|||||||
"valve_entity2_id": "Entity id de la 2ème valve",
|
"valve_entity2_id": "Entity id de la 2ème valve",
|
||||||
"valve_entity3_id": "Entity id de la 3ème valve",
|
"valve_entity3_id": "Entity id de la 3ème valve",
|
||||||
"valve_entity4_id": "Entity id de la 4è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": {
|
"tpi": {
|
||||||
@@ -203,7 +207,9 @@
|
|||||||
"valve_entity2_id": "2ème valve",
|
"valve_entity2_id": "2ème valve",
|
||||||
"valve_entity3_id": "3ème valve",
|
"valve_entity3_id": "3ème valve",
|
||||||
"valve_entity4_id": "4è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": {
|
"data_description": {
|
||||||
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
"heater_entity_id": "Entity id du 1er radiateur obligatoire",
|
||||||
@@ -220,7 +226,9 @@
|
|||||||
"valve_entity2_id": "Entity id de la 2ème valve",
|
"valve_entity2_id": "Entity id de la 2ème valve",
|
||||||
"valve_entity3_id": "Entity id de la 3ème valve",
|
"valve_entity3_id": "Entity id de la 3ème valve",
|
||||||
"valve_entity4_id": "Entity id de la 4è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": {
|
"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.const import UnitOfTemperature, STATE_ON, STATE_OFF, ATTR_TEMPERATURE
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.util import dt as dt_util
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.components.climate import (
|
from homeassistant.components.climate import (
|
||||||
ClimateEntity,
|
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.base_thermostat import BaseThermostat
|
||||||
from custom_components.versatile_thermostat.const import * # pylint: disable=wildcard-import, unused-wildcard-import
|
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.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
|
from .const import ( # pylint: disable=unused-import
|
||||||
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
||||||
@@ -478,13 +478,6 @@ async def send_presence_change_event(
|
|||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
return ret
|
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(
|
async def send_climate_change_event(
|
||||||
entity: BaseThermostat,
|
entity: BaseThermostat,
|
||||||
new_hvac_mode: HVACMode,
|
new_hvac_mode: HVACMode,
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ from custom_components.versatile_thermostat.const import (
|
|||||||
CONF_AUTO_REGULATION_MODE,
|
CONF_AUTO_REGULATION_MODE,
|
||||||
CONF_AUTO_REGULATION_MEDIUM,
|
CONF_AUTO_REGULATION_MEDIUM,
|
||||||
CONF_AUTO_REGULATION_NONE,
|
CONF_AUTO_REGULATION_NONE,
|
||||||
|
CONF_AUTO_REGULATION_DTEMP,
|
||||||
|
CONF_AUTO_REGULATION_PERIOD_MIN
|
||||||
)
|
)
|
||||||
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
||||||
CONF_NAME: "TheOverSwitchMockName",
|
CONF_NAME: "TheOverSwitchMockName",
|
||||||
@@ -125,7 +127,9 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
|
|||||||
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
||||||
CONF_CLIMATE: "climate.mock_climate",
|
CONF_CLIMATE: "climate.mock_climate",
|
||||||
CONF_AC_MODE: False,
|
CONF_AC_MODE: False,
|
||||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM
|
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM,
|
||||||
|
CONF_AUTO_REGULATION_DTEMP: 0.5,
|
||||||
|
CONF_AUTO_REGULATION_PERIOD_MIN: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
MOCK_TH_OVER_CLIMATE_TYPE_NOT_REGULATED_CONFIG = {
|
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 = {
|
MOCK_TH_OVER_CLIMATE_TYPE_AC_CONFIG = {
|
||||||
CONF_CLIMATE: "climate.mock_climate",
|
CONF_CLIMATE: "climate.mock_climate",
|
||||||
CONF_AC_MODE: True,
|
CONF_AC_MODE: True,
|
||||||
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM
|
CONF_AUTO_REGULATION_MODE: CONF_AUTO_REGULATION_MEDIUM,
|
||||||
|
CONF_AUTO_REGULATION_DTEMP: 0.5,
|
||||||
|
CONF_AUTO_REGULATION_PERIOD_MIN: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
MOCK_PRESETS_CONFIG = {
|
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_tasks", [True])
|
||||||
@pytest.mark.parametrize("expected_lingering_timers", [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"""
|
"""Test the regulation of an over climate thermostat"""
|
||||||
|
|
||||||
entry = MockConfigEntry(
|
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", {})
|
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||||
|
|
||||||
# Creates the regulated VTherm over climate
|
# Creates the regulated VTherm over climate
|
||||||
|
# change temperature so that the heating will start
|
||||||
|
event_timestamp = now - timedelta(minutes=10)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||||
), patch(
|
), patch(
|
||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||||
return_value=fake_underlying_climate,
|
return_value=fake_underlying_climate,
|
||||||
@@ -73,45 +76,48 @@ async def test_over_climate_regulation(hass: HomeAssistant, skip_hass_states_is_
|
|||||||
]
|
]
|
||||||
assert entity.preset_mode is PRESET_NONE
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
|
||||||
# Activate the heating by changing HVACMode and temperature
|
# Activate the heating by changing HVACMode and temperature
|
||||||
with patch(
|
|
||||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
|
||||||
):
|
|
||||||
# Select a hvacmode, presence and preset
|
# Select a hvacmode, presence and preset
|
||||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||||
assert entity.hvac_mode is HVACMode.HEAT
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
assert entity.hvac_action == HVACAction.OFF
|
assert entity.hvac_action == HVACAction.OFF
|
||||||
|
|
||||||
# change temperature so that the heating will start
|
assert entity.regulated_target_temp is entity.min_temp
|
||||||
event_timestamp = now - timedelta(minutes=10)
|
|
||||||
await send_temperature_change_event(entity, 15, event_timestamp)
|
await send_temperature_change_event(entity, 15, event_timestamp)
|
||||||
await send_ext_temperature_change_event(entity, 10, 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
|
fake_underlying_climate.set_hvac_action(HVACAction.HEATING) # simulate under heating
|
||||||
await entity.async_set_temperature(temperature=18)
|
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
|
# the regulated temperature should be greater
|
||||||
assert entity.hvac_action == HVACAction.HEATING
|
assert entity.regulated_target_temp > entity.target_temperature
|
||||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
assert entity.regulated_target_temp == 18+2.2 # 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
|
|
||||||
assert entity.regulated_target_temp == 18+2.9 # In medium we could go up to +3 degre
|
|
||||||
assert entity.hvac_action == HVACAction.HEATING
|
|
||||||
|
|
||||||
# change temperature so that the regulated temperature should slow down
|
# change temperature so that the regulated temperature should slow down
|
||||||
event_timestamp = now - timedelta(minutes=9)
|
event_timestamp = now - timedelta(minutes=5)
|
||||||
await send_temperature_change_event(entity, 19, event_timestamp)
|
with patch(
|
||||||
await send_ext_temperature_change_event(entity, 18, event_timestamp)
|
"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
|
# the regulated temperature should be under
|
||||||
assert entity.regulated_target_temp < entity.target_temperature
|
assert entity.regulated_target_temp < entity.target_temperature
|
||||||
assert entity.regulated_target_temp == 18-0.1
|
assert entity.regulated_target_temp == 18-0.6
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@pytest.mark.parametrize("expected_lingering_timers", [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"""
|
"""Test the regulation of an over climate thermostat"""
|
||||||
|
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
@@ -128,8 +134,11 @@ async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_st
|
|||||||
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||||
|
|
||||||
# Creates the regulated VTherm over climate
|
# Creates the regulated VTherm over climate
|
||||||
|
# change temperature so that the heating will start
|
||||||
|
event_timestamp = now - timedelta(minutes=10)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
"custom_components.versatile_thermostat.commons.NowClass.get_now", return_value=event_timestamp
|
||||||
), patch(
|
), patch(
|
||||||
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
"custom_components.versatile_thermostat.underlyings.UnderlyingClimate.find_underlying_climate",
|
||||||
return_value=fake_underlying_climate,
|
return_value=fake_underlying_climate,
|
||||||
@@ -164,47 +173,53 @@ async def test_over_climate_regulation_ac_mode(hass: HomeAssistant, skip_hass_st
|
|||||||
]
|
]
|
||||||
assert entity.preset_mode is PRESET_NONE
|
assert entity.preset_mode is PRESET_NONE
|
||||||
|
|
||||||
# Activate the heating by changing HVACMode and temperature
|
# Activate the heating by changing HVACMode and temperature
|
||||||
with patch(
|
|
||||||
"custom_components.versatile_thermostat.base_thermostat.BaseThermostat.send_event"
|
|
||||||
):
|
|
||||||
# Select a hvacmode, presence and preset
|
# Select a hvacmode, presence and preset
|
||||||
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
await entity.async_set_hvac_mode(HVACMode.HEAT)
|
||||||
assert entity.hvac_mode is HVACMode.HEAT
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
assert entity.hvac_action == HVACAction.OFF
|
assert entity.hvac_action == HVACAction.OFF
|
||||||
|
|
||||||
# change temperature so that the heating will start
|
# 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_temperature_change_event(entity, 30, event_timestamp)
|
||||||
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
||||||
|
|
||||||
|
|
||||||
# set manual target temp
|
# 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
|
fake_underlying_climate.set_hvac_action(HVACAction.COOLING) # simulate under heating
|
||||||
assert entity.hvac_action == HVACAction.COOLING
|
assert entity.hvac_action == HVACAction.COOLING
|
||||||
assert entity.preset_mode == PRESET_NONE # Manual mode
|
assert entity.preset_mode == PRESET_NONE # Manual mode
|
||||||
|
|
||||||
# the regulated temperature should be lower
|
# the regulated temperature should be lower
|
||||||
assert entity.regulated_target_temp < entity.target_temperature
|
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.regulated_target_temp == 25-3 # In medium we could go up to -3 degre
|
||||||
assert entity.hvac_action == HVACAction.COOLING
|
assert entity.hvac_action == HVACAction.COOLING
|
||||||
|
|
||||||
# change temperature so that the regulated temperature should slow down
|
# change temperature so that the regulated temperature should slow down
|
||||||
event_timestamp = now - timedelta(minutes=9)
|
event_timestamp = now - timedelta(minutes=5)
|
||||||
await send_temperature_change_event(entity, 26, event_timestamp)
|
with patch(
|
||||||
await send_ext_temperature_change_event(entity, 35, event_timestamp)
|
"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
|
# the regulated temperature should be under
|
||||||
assert entity.regulated_target_temp < entity.target_temperature
|
assert entity.regulated_target_temp < entity.target_temperature
|
||||||
assert entity.regulated_target_temp == 25-2.7
|
assert entity.regulated_target_temp == 25-2.3
|
||||||
|
|
||||||
# change temperature so that the regulated temperature should slow down
|
# change temperature so that the regulated temperature should slow down
|
||||||
event_timestamp = now - timedelta(minutes=9)
|
event_timestamp = now - timedelta(minutes=3)
|
||||||
await send_temperature_change_event(entity, 20, event_timestamp)
|
with patch(
|
||||||
await send_ext_temperature_change_event(entity, 30, event_timestamp)
|
"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
|
# the regulated temperature should be greater
|
||||||
assert entity.regulated_target_temp > entity.target_temperature
|
assert entity.regulated_target_temp > entity.target_temperature
|
||||||
assert entity.regulated_target_temp == 25+1.8
|
assert entity.regulated_target_temp == 25+0.4
|
||||||
|
|||||||
Reference in New Issue
Block a user