Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9269240fe3 | ||
|
|
91ba2387b2 | ||
|
|
162efb4709 | ||
|
|
6a97622226 |
@@ -7,7 +7,7 @@ import logging
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
from typing import Any, TypeVar, Generic
|
||||
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.core import (
|
||||
@@ -140,6 +140,7 @@ from .ema import ExponentialMovingAverage
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
ConfigData = MappingProxyType[str, Any]
|
||||
T = TypeVar("T", bound=UnderlyingEntity)
|
||||
|
||||
|
||||
def get_tz(hass: HomeAssistant):
|
||||
@@ -148,7 +149,7 @@ def get_tz(hass: HomeAssistant):
|
||||
return dt_util.get_time_zone(hass.config.time_zone)
|
||||
|
||||
|
||||
class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
||||
"""Representation of a base class for all Versatile Thermostat device."""
|
||||
|
||||
_entity_component_unrecorded_attributes = (
|
||||
@@ -278,7 +279,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity):
|
||||
|
||||
self._last_change_time = None
|
||||
|
||||
self._underlyings: list[UnderlyingEntity] = []
|
||||
self._underlyings: list[T] = []
|
||||
|
||||
self._ema_temp = None
|
||||
self._ema_algo = None
|
||||
|
||||
@@ -39,6 +39,7 @@ from .const import (
|
||||
CONF_USE_WINDOW_FEATURE,
|
||||
CONF_THERMOSTAT_TYPE,
|
||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||
CONF_USE_CENTRAL_BOILER_FEATURE,
|
||||
CONF_CENTRAL_BOILER_ACTIVATION_SRV,
|
||||
CONF_CENTRAL_BOILER_DEACTIVATION_SRV,
|
||||
overrides,
|
||||
@@ -63,10 +64,13 @@ async def async_setup_entry(
|
||||
name = entry.data.get(CONF_NAME)
|
||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||
|
||||
entities = None
|
||||
|
||||
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||
entities = [
|
||||
CentralBoilerBinarySensor(hass, unique_id, name, entry.data),
|
||||
]
|
||||
if entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE):
|
||||
entities = [
|
||||
CentralBoilerBinarySensor(hass, unique_id, name, entry.data),
|
||||
]
|
||||
else:
|
||||
entities = [
|
||||
SecurityBinarySensor(hass, unique_id, name, entry.data),
|
||||
@@ -81,7 +85,8 @@ async def async_setup_entry(
|
||||
if entry.data.get(CONF_USE_POWER_FEATURE):
|
||||
entities.append(OverpoweringBinarySensor(hass, unique_id, name, entry.data))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
if entities:
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class SecurityBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
|
||||
|
||||
@@ -242,6 +242,14 @@ class VersatileThermostatBaseConfigFlow(FlowHandler):
|
||||
and infos.get(CONF_PRESENCE_SENSOR, None) is None
|
||||
):
|
||||
return False
|
||||
|
||||
if self._infos[CONF_USE_CENTRAL_BOILER_FEATURE] and (
|
||||
not self._infos.get(CONF_CENTRAL_BOILER_ACTIVATION_SRV, False)
|
||||
or len(self._infos.get(CONF_CENTRAL_BOILER_ACTIVATION_SRV)) <= 0
|
||||
or not self._infos.get(CONF_CENTRAL_BOILER_DEACTIVATION_SRV, False)
|
||||
or len(self._infos.get(CONF_CENTRAL_BOILER_DEACTIVATION_SRV)) <= 0
|
||||
):
|
||||
return False
|
||||
else:
|
||||
if (
|
||||
infos.get(CONF_NAME) is None
|
||||
@@ -857,6 +865,9 @@ class VersatileThermostatOptionsFlowHandler(
|
||||
if not self._infos[CONF_USE_PRESENCE_FEATURE]:
|
||||
self._infos[CONF_USE_PRESENCE_CENTRAL_CONFIG] = False
|
||||
self._infos[CONF_PRESENCE_SENSOR] = None
|
||||
if not self._infos[CONF_USE_CENTRAL_BOILER_FEATURE]:
|
||||
self._infos[CONF_CENTRAL_BOILER_ACTIVATION_SRV] = None
|
||||
self._infos[CONF_CENTRAL_BOILER_DEACTIVATION_SRV] = None
|
||||
|
||||
_LOGGER.info(
|
||||
"Recreating entry %s due to configuration change. New config is now: %s",
|
||||
|
||||
@@ -24,11 +24,16 @@ class IntervalCaller:
|
||||
Convenience wrapper around Home Assistant's `async_track_time_interval` function.
|
||||
"""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, interval_sec: int) -> None:
|
||||
def __init__(self, hass: HomeAssistant, interval_sec: float) -> None:
|
||||
self._hass = hass
|
||||
self._interval_sec = interval_sec
|
||||
self._remove_handle: CALLBACK_TYPE | None = None
|
||||
|
||||
@property
|
||||
def interval_sec(self) -> float:
|
||||
"""Return the calling interval in seconds."""
|
||||
return self._interval_sec
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the regular calls to the action function."""
|
||||
if self._remove_handle:
|
||||
@@ -43,7 +48,11 @@ class IntervalCaller:
|
||||
|
||||
async def callback(_time: datetime):
|
||||
try:
|
||||
_LOGGER.debug("Calling keep-alive action")
|
||||
_LOGGER.debug(
|
||||
"Calling keep-alive action '%s' (%ss interval)",
|
||||
action.__name__,
|
||||
self._interval_sec,
|
||||
)
|
||||
await action()
|
||||
except Exception as e: # pylint: disable=broad-exception-caught
|
||||
_LOGGER.error(e)
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"quality_scale": "silver",
|
||||
"requirements": [],
|
||||
"ssdp": [],
|
||||
"version": "6.0.2",
|
||||
"version": "6.0.4",
|
||||
"zeroconf": []
|
||||
}
|
||||
@@ -51,6 +51,7 @@ from .const import (
|
||||
CONF_USE_PRESETS_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_CENTRAL_CONFIG,
|
||||
CONF_USE_PRESENCE_FEATURE,
|
||||
CONF_USE_CENTRAL_BOILER_FEATURE,
|
||||
overrides,
|
||||
)
|
||||
|
||||
@@ -153,9 +154,10 @@ async def async_setup_entry(
|
||||
|
||||
# For central config only
|
||||
else:
|
||||
entities.append(
|
||||
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data)
|
||||
)
|
||||
if entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE):
|
||||
entities.append(
|
||||
ActivateBoilerThresholdNumber(hass, unique_id, name, entry.data)
|
||||
)
|
||||
for preset in CONF_PRESETS_WITH_AC_VALUES:
|
||||
_LOGGER.debug(
|
||||
"%s - configuring Number central, AC, non AWAY for preset %s",
|
||||
@@ -178,7 +180,8 @@ async def async_setup_entry(
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities, True)
|
||||
if len(entities) > 0:
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class ActivateBoilerThresholdNumber(
|
||||
|
||||
@@ -79,7 +79,6 @@ async def async_setup_entry(
|
||||
entities = [
|
||||
NbActiveDeviceForBoilerSensor(hass, unique_id, name, entry.data)
|
||||
]
|
||||
async_add_entities(entities, True)
|
||||
else:
|
||||
entities = [
|
||||
LastTemperatureSensor(hass, unique_id, name, entry.data),
|
||||
@@ -108,6 +107,7 @@ async def async_setup_entry(
|
||||
RegulatedTemperatureSensor(hass, unique_id, name, entry.data)
|
||||
)
|
||||
|
||||
if entities:
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ from .underlyings import UnderlyingClimate
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ThermostatOverClimate(BaseThermostat):
|
||||
class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
||||
"""Representation of a base class for a Versatile Thermostat over a climate"""
|
||||
|
||||
_auto_regulation_mode: str | None = None
|
||||
|
||||
@@ -27,7 +27,7 @@ from .prop_algorithm import PropAlgorithm
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ThermostatOverSwitch(BaseThermostat):
|
||||
class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]):
|
||||
"""Representation of a base class for a Versatile Thermostat over a switch."""
|
||||
|
||||
_entity_component_unrecorded_attributes = (
|
||||
@@ -136,11 +136,11 @@ class ThermostatOverSwitch(BaseThermostat):
|
||||
"""Custom attributes"""
|
||||
super().update_custom_attributes()
|
||||
|
||||
under0: UnderlyingSwitch = self._underlyings[0]
|
||||
self._attr_extra_state_attributes["is_over_switch"] = self.is_over_switch
|
||||
self._attr_extra_state_attributes["is_inversed"] = self.is_inversed
|
||||
self._attr_extra_state_attributes["underlying_switch_0"] = self._underlyings[
|
||||
0
|
||||
].entity_id
|
||||
self._attr_extra_state_attributes["keep_alive_sec"] = under0.keep_alive_sec
|
||||
self._attr_extra_state_attributes["underlying_switch_0"] = under0.entity_id
|
||||
self._attr_extra_state_attributes["underlying_switch_1"] = (
|
||||
self._underlyings[1].entity_id if len(self._underlyings) > 1 else None
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ from .underlyings import UnderlyingValve
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ThermostatOverValve(BaseThermostat): # pylint: disable=abstract-method
|
||||
class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=abstract-method
|
||||
"""Representation of a class for a Versatile Thermostat over a Valve"""
|
||||
|
||||
_entity_component_unrecorded_attributes = (
|
||||
|
||||
@@ -188,7 +188,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
thermostat: Any,
|
||||
switch_entity_id: str,
|
||||
initial_delay_sec: int,
|
||||
keep_alive_sec: int,
|
||||
keep_alive_sec: float,
|
||||
) -> None:
|
||||
"""Initialize the underlying switch"""
|
||||
|
||||
@@ -217,6 +217,11 @@ class UnderlyingSwitch(UnderlyingEntity):
|
||||
"""Tells if the switch command should be inversed"""
|
||||
return self._thermostat.is_inversed
|
||||
|
||||
@property
|
||||
def keep_alive_sec(self) -> float:
|
||||
"""Return the switch keep-alive interval in seconds."""
|
||||
return self._keep_alive.interval_sec
|
||||
|
||||
@overrides
|
||||
def startup(self):
|
||||
super().startup()
|
||||
|
||||
@@ -94,8 +94,8 @@ async def test_add_a_central_config(hass: HomeAssistant, skip_hass_states_is_sta
|
||||
assert api.nb_active_device_for_boiler_entity is None
|
||||
assert api.nb_active_device_for_boiler is None
|
||||
|
||||
assert api.nb_active_device_for_boiler_threshold_entity is not None
|
||||
assert api.nb_active_device_for_boiler_threshold == 1 # the default value
|
||||
assert api.nb_active_device_for_boiler_threshold_entity is None
|
||||
assert api.nb_active_device_for_boiler_threshold is None
|
||||
|
||||
|
||||
# @pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||
|
||||
Reference in New Issue
Block a user