Skip to content

Commit

Permalink
All tests ok for central_feature_power_manager
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-Marc Collin committed Jan 3, 2025
1 parent 9f3199a commit 03fbc53
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 97 deletions.
24 changes: 14 additions & 10 deletions custom_components/versatile_thermostat/base_thermostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ def __init__(

self._ema_temp = None
self._ema_algo = None
self._now = None

self._attr_fan_mode = None

Expand Down Expand Up @@ -1582,15 +1581,6 @@ def save_all():
await self.async_set_hvac_mode(HVACMode.OFF)
return

def _set_now(self, now: datetime):
"""Set the now timestamp. This is only for tests purpose"""
self._now = now

@property
def now(self) -> datetime:
"""Get now. The local datetime or the overloaded _set_now date"""
return self._now if self._now is not None else NowClass.get_now(self._hass)

@property
def is_initialized(self) -> bool:
"""Check if all underlyings are initialized
Expand Down Expand Up @@ -1965,3 +1955,17 @@ async def async_turn_on(self) -> None:
def is_preset_configured(self, preset) -> bool:
"""Returns True if the preset in argument is configured"""
return self._presets.get(preset, None) is not None

# For testing purpose
@DeprecationWarning
def _set_now(self, now: datetime):
"""Set the now timestamp. This is only for tests purpose
This method should be replaced by the vthermAPI equivalent"""
VersatileThermostatAPI.get_vtherm_api(self._hass).set_now(now)

@property
@DeprecationWarning
def now(self) -> datetime:
"""Get now. The local datetime or the overloaded _set_now date
This method should be replaced by the vthermAPI equivalent"""
return VersatileThermostatAPI.get_vtherm_api(self._hass).now
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
# circular dependency
# from .base_thermostat import BaseThermostat

MIN_DTEMP_SECS = 60

_LOGGER = logging.getLogger(__name__)


Expand All @@ -44,6 +46,7 @@ def __init__(self, hass: HomeAssistant, vtherm_api: Any):
self._current_power: float = None
self._current_max_power: float = None
self._power_temp: float = None
self._last_shedding_date = None

def post_init(self, entry_infos: ConfigData):
"""Gets the configuration parameters"""
Expand Down Expand Up @@ -103,65 +106,26 @@ async def _power_sensor_changed(self, event: Event[EventStateChangedData]):
"""Handle power changes."""
_LOGGER.debug("Thermostat %s - Receive new Power event", self)
_LOGGER.debug(event)
new_state = event.data.get("new_state")
old_state = event.data.get("old_state")
if (
new_state is None
or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
or (old_state is not None and new_state.state == old_state.state)
):
return

try:
current_power = float(new_state.state)
if math.isnan(current_power) or math.isinf(current_power):
raise ValueError(f"Sensor has illegal state {new_state.state}")
self._current_power = current_power

if self._vtherm.preset_mode == PRESET_POWER:
await self._vtherm.async_control_heating()

except ValueError as ex:
_LOGGER.error("Unable to update current_power from sensor: %s", ex)
self.refresh_state()

@callback
async def _max_power_sensor_changed(self, event: Event[EventStateChangedData]):
"""Handle power max changes."""
_LOGGER.debug("Thermostat %s - Receive new Power Max event", self.name)
_LOGGER.debug(event)
new_state = event.data.get("new_state")
old_state = event.data.get("old_state")
if (
new_state is None
or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN)
or (old_state is not None and new_state.state == old_state.state)
):
return

try:
current_power_max = float(new_state.state)
if math.isnan(current_power_max) or math.isinf(current_power_max):
raise ValueError(f"Sensor has illegal state {new_state.state}")
self._current_max_power = current_power_max
if self._vtherm.preset_mode == PRESET_POWER:
await self._vtherm.async_control_heating()

except ValueError as ex:
_LOGGER.error("Unable to update current_power from sensor: %s", ex)
self.refresh_state()

@overrides
async def refresh_state(self) -> bool:
def refresh_state(self) -> bool:
"""Tries to get the last state from sensor
Returns True if a change has been made"""
ret = False
if self._is_configured:
# try to acquire current power and power max
current_power_state = self.hass.states.get(self._power_sensor_entity_id)
if current_power_state and current_power_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_power = float(current_power_state.state)
if (
new_state := get_safe_float(self._hass, self._power_sensor_entity_id)
) is not None:
self._current_power = new_state
_LOGGER.debug(
"%s - Current power have been retrieved: %.3f",
self,
Expand All @@ -170,21 +134,31 @@ async def refresh_state(self) -> bool:
ret = True

# Try to acquire power max
current_power_max_state = self.hass.states.get(
self._max_power_sensor_entity_id
)
if current_power_max_state and current_power_max_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_max_power = float(current_power_max_state.state)
if (
new_state := get_safe_float(
self._hass, self._max_power_sensor_entity_id
)
) is not None:
self._current_max_power = new_state
_LOGGER.debug(
"%s - Current power max have been retrieved: %.3f",
self,
self._current_max_power,
)
ret = True

# check if we need to re-calculate shedding
if ret:
now = self._vtherm_api.now
dtimestamp = (
(now - self._last_shedding_date).seconds
if self._last_shedding_date
else 999
)
if dtimestamp >= MIN_DTEMP_SECS:
self.calculate_shedding()
self._last_shedding_date = now

return ret

async def calculate_shedding(self):
Expand Down
2 changes: 2 additions & 0 deletions custom_components/versatile_thermostat/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@ def get_safe_float(hass, entity_id: str):
if (
entity_id is None
or not (state := hass.states.get(entity_id))
or state.state is None
or state.state == "None"
or state.state == "unknown"
or state.state == "unavailable"
):
Expand Down
15 changes: 15 additions & 0 deletions custom_components/versatile_thermostat/vtherm_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" The API of Versatile Thermostat"""

import logging
from datetime import datetime
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry

Expand All @@ -16,6 +17,7 @@
CONF_THERMOSTAT_TYPE,
CONF_THERMOSTAT_CENTRAL_CONFIG,
CONF_MAX_ON_PERCENT,
NowClass,
)

from .central_feature_power_manager import CentralFeaturePowerManager
Expand Down Expand Up @@ -68,6 +70,9 @@ def __init__(self) -> None:
VersatileThermostatAPI._hass, self
)

# the current time (for testing purpose)
self._now = None

def find_central_configuration(self):
"""Search for a central configuration"""
if not self._central_configuration:
Expand Down Expand Up @@ -303,3 +308,13 @@ def hass(self):
def central_power_manager(self) -> any:
"""Returns the central power manager"""
return self._central_power_manager

# For testing purpose
def _set_now(self, now: datetime):
"""Set the now timestamp. This is only for tests purpose"""
self._now = now

@property
def now(self) -> datetime:
"""Get now. The local datetime or the overloaded _set_now date"""
return self._now if self._now is not None else NowClass.get_now(self._hass)
Loading

0 comments on commit 03fbc53

Please sign in to comment.