[Feature Request] - Pwoer shedding should prevent all VTherm to start at the same time (#845)

Fixes #844

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
This commit is contained in:
Jean-Marc Collin
2025-01-19 20:45:37 +01:00
committed by GitHub
parent 8743872c09
commit 05fe2055e2
9 changed files with 261 additions and 145 deletions

View File

@@ -1528,8 +1528,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
is_window_detected = self._window_manager.is_window_detected
if new_central_mode == CENTRAL_MODE_AUTO:
if not is_window_detected and not first_init:
await self.restore_hvac_mode()
await self.restore_preset_mode()
await self.restore_preset_mode(force=False)
await self.restore_hvac_mode(need_control_heating=True)
elif is_window_detected and self.hvac_mode == HVACMode.OFF:
# do not restore but mark the reason of off with window detection
self.set_hvac_off_reason(HVAC_OFF_REASON_WINDOW_DETECTION)

View File

@@ -47,6 +47,7 @@ class CentralFeaturePowerManager(BaseFeatureManager):
self._current_max_power: float = None
self._power_temp: float = None
self._cancel_calculate_shedding_call = None
self._started_vtherm_total_power: float = None
# Not used now
self._last_shedding_date = None
@@ -71,6 +72,7 @@ class CentralFeaturePowerManager(BaseFeatureManager):
and self._power_temp
):
self._is_configured = True
self._started_vtherm_total_power = 0
else:
_LOGGER.info("Power management is not fully configured and will be deactivated")
@@ -102,6 +104,8 @@ class CentralFeaturePowerManager(BaseFeatureManager):
"""Handle power changes."""
_LOGGER.debug("Receive new Power event")
_LOGGER.debug(event)
self._started_vtherm_total_power = 0
await self.refresh_state()
@callback
@@ -275,6 +279,12 @@ class CentralFeaturePowerManager(BaseFeatureManager):
vtherms.sort(key=cmp_to_key(cmp_temps))
return vtherms
def add_started_vtherm_total_power(self, started_power: float):
"""Add the power into the _started_vtherm_total_power which holds all VTherm started after
the last power measurement"""
self._started_vtherm_total_power += started_power
_LOGGER.debug("%s - started_vtherm_total_power is now %s", self, self._started_vtherm_total_power)
@property
def is_configured(self) -> bool:
"""True if the FeatureManager is fully configured"""
@@ -305,5 +315,10 @@ class CentralFeaturePowerManager(BaseFeatureManager):
"""Return the max power sensor entity id"""
return self._max_power_sensor_entity_id
@property
def started_vtherm_total_power(self) -> float | None:
"""Return the started_vtherm_total_power"""
return self._started_vtherm_total_power
def __str__(self):
return "CentralPowerManager"

View File

@@ -104,7 +104,8 @@ class FeaturePowerManager(BaseFeatureManager):
async def check_power_available(self) -> bool:
"""Check if the Vtherm can be started considering overpowering.
Returns True if no overpowering conditions are found
Returns True if no overpowering conditions are found.
If True the vtherm power is written into the temporay vtherm started
"""
vtherm_api = VersatileThermostatAPI.get_vtherm_api()
@@ -116,6 +117,7 @@ class FeaturePowerManager(BaseFeatureManager):
current_power = vtherm_api.central_power_manager.current_power
current_max_power = vtherm_api.central_power_manager.current_max_power
started_vtherm_total_power = vtherm_api.central_power_manager.started_vtherm_total_power
if (
current_power is None
or current_max_power is None
@@ -146,7 +148,7 @@ class FeaturePowerManager(BaseFeatureManager):
self._device_power * self._vtherm.proportional_algorithm.on_percent,
)
ret = (current_power + power_consumption_max) < current_max_power
ret = (current_power + started_vtherm_total_power + power_consumption_max) < current_max_power
if not ret:
_LOGGER.info(
"%s - there is not enough power available power=%.3f, max_power=%.3f heater power=%.3f",
@@ -155,6 +157,10 @@ class FeaturePowerManager(BaseFeatureManager):
current_max_power,
self._device_power,
)
else:
# Adds the current_power_max to the started vtherm total power
vtherm_api.central_power_manager.add_started_vtherm_total_power(power_consumption_max)
return ret
async def set_overpowering(self, overpowering: bool, power_consumption_max=0):