Expose the keep_alive_sec attribute in HASS Developer Tools - States (#381)

* Typing: Make BaseThermostat generic on the UnderlyingEntity type

* Typing: Change the type of IntervalCaller._interval_sec from int to float

This makes the IntervalCaller class more reusable.

* Keep-alive: Expose UnderlyingSwitch.keep_alive_sec as a HASS Dev Tools attribute

Also improve a keep-alive log message.
This commit is contained in:
Paulo Ferreira de Castro
2024-03-23 10:49:09 +00:00
committed by GitHub
parent 5db7a49e75
commit 6a97622226
6 changed files with 27 additions and 12 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
)

View File

@@ -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 = (

View File

@@ -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()