From 7e7d0c18c2a3512c81504dc426d9f3b5407870ed Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Wed, 27 Nov 2024 18:40:15 +0000 Subject: [PATCH] Try to fix the central boiler calculation --- .../versatile_thermostat/base_thermostat.py | 5 ++ .../versatile_thermostat/sensor.py | 66 +++++++++++++++---- .../thermostat_climate_valve.py | 5 ++ tests/test_central_config.py | 4 ++ 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/custom_components/versatile_thermostat/base_thermostat.py b/custom_components/versatile_thermostat/base_thermostat.py index dc1d21f..7ae0b12 100644 --- a/custom_components/versatile_thermostat/base_thermostat.py +++ b/custom_components/versatile_thermostat/base_thermostat.py @@ -1132,6 +1132,11 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): """Returns the underlying entities""" return self._underlyings + @property + def activable_underlying_entities(self) -> list | None: + """Returns the activable underlying entities for controling the central boiler""" + return self.underlying_entities + def find_underlying_by_entity_id(self, entity_id: str) -> Entity | None: """Get the underlying entity by a entity_id""" for under in self._underlyings: diff --git a/custom_components/versatile_thermostat/sensor.py b/custom_components/versatile_thermostat/sensor.py index 02d3b1d..11bcda0 100644 --- a/custom_components/versatile_thermostat/sensor.py +++ b/custom_components/versatile_thermostat/sensor.py @@ -3,7 +3,7 @@ import logging import math -from homeassistant.core import HomeAssistant, callback, Event, CoreState +from homeassistant.core import HomeAssistant, callback, Event, CoreState, State from homeassistant.const import ( UnitOfTime, @@ -23,13 +23,13 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.event import async_track_state_change_event +from homeassistant.helpers.event import ( + async_track_state_change_event, +) from homeassistant.components.climate import ( ClimateEntity, DOMAIN as CLIMATE_DOMAIN, - HVACAction, - HVACMode, ) @@ -708,7 +708,7 @@ class NbActiveDeviceForBoilerSensor(SensorEntity): for entity in component.entities: if isinstance(entity, BaseThermostat) and entity.is_used_by_central_boiler: self._entities.append(entity) - for under in entity.underlying_entities: + for under in entity.activable_underlying_entities: underlying_entities_id.append(under.entity_id) if len(underlying_entities_id) > 0: # Arme l'écoute de la première entité @@ -728,21 +728,59 @@ class NbActiveDeviceForBoilerSensor(SensorEntity): await self.calculate_nb_active_devices(None) - async def calculate_nb_active_devices(self, _): + async def calculate_nb_active_devices(self, event: Event): """Calculate the number of active VTherm that have an influence on central boiler""" - _LOGGER.debug( - "%s - calculating the number of active underlying device for boiler activation", - self, - ) + # _LOGGER.debug("%s- calculate_nb_active_devices - the event is %s ", self, event) + + if event is not None: + new_state: State = event.data.get("new_state") + # _LOGGER.debug( + # "%s - calculate_nb_active_devices new_state is %s", self, new_state + # ) + if not new_state: + return + + old_state: State = event.data.get("old_state") + + # For underlying climate, we need to observe also the hvac_action if available + new_hvac_action = new_state.attributes.get("hvac_action") + old_hvac_action = ( + old_state.attributes.get("hvac_action") + if old_state is not None + else None + ) + + # Filter events that are not interested for us + if ( + old_state is not None + and new_state.state == old_state.state + and new_hvac_action == old_hvac_action + ): + # A false state change + return + + _LOGGER.debug( + "%s - calculating the number of active underlying device for boiler activation. change change from %s to %s", + self, + old_state, + new_state, + ) + else: + _LOGGER.debug( + "%s - calculating the number of active underlying device for boiler activation. First time calculation", + self, + ) + nb_active = 0 for entity in self._entities: - _LOGGER.debug( - "Examining the hvac_action of %s", - entity.name, - ) nb_active += entity.nb_device_actives + _LOGGER.debug( + "After examining the hvac_action of %s, nb_active is %s", + entity.name, + nb_active, + ) self._attr_native_value = nb_active _LOGGER.debug( diff --git a/custom_components/versatile_thermostat/thermostat_climate_valve.py b/custom_components/versatile_thermostat/thermostat_climate_valve.py index 56ac00e..ad0b0ca 100644 --- a/custom_components/versatile_thermostat/thermostat_climate_valve.py +++ b/custom_components/versatile_thermostat/thermostat_climate_valve.py @@ -284,6 +284,11 @@ class ThermostatOverClimateValve(ThermostatOverClimate): else: return 0 + @property + def activable_underlying_entities(self) -> list | None: + """Returns the activable underlying entities for controling the central boiler""" + return self._underlyings_valve_regulation + @overrides async def service_set_auto_regulation_mode(self, auto_regulation_mode: str): """This should not be possible in valve regulation mode""" diff --git a/tests/test_central_config.py b/tests/test_central_config.py index fa22da4..b74c51b 100644 --- a/tests/test_central_config.py +++ b/tests/test_central_config.py @@ -15,6 +15,10 @@ from custom_components.versatile_thermostat.thermostat_switch import ( ThermostatOverSwitch, ) +from custom_components.versatile_thermostat.thermostat_climate import ( + ThermostatOverClimate, +) + from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import