diff --git a/custom_components/peaqhvac/service/hvac/water_heater/models/next_water_boost_model.py b/custom_components/peaqhvac/service/hvac/water_heater/models/next_water_boost_model.py index dfb92487..9fa12c85 100644 --- a/custom_components/peaqhvac/service/hvac/water_heater/models/next_water_boost_model.py +++ b/custom_components/peaqhvac/service/hvac/water_heater/models/next_water_boost_model.py @@ -54,23 +54,30 @@ def get_demand(temp) -> Demand: return Demand.ErrorDemand - - @dataclass class NextWaterBoostModel: prices: list = field(default_factory=lambda: []) min_price: float = None # type: ignore - groups: list = field(default_factory=lambda: []) - non_hours: set = field(default_factory=lambda: []) - demand_hours: set = field(default_factory=lambda: {}) + preset: HvacPresets = HvacPresets.Normal now_dt: datetime = None # type: ignore latest_boost: datetime = None # type: ignore - floating_mean: float = None # type: ignore + temp_trend: float = None # type: ignore current_temp: float = None # type: ignore target_temp: float = None # type: ignore + floating_mean: float = field(default=None, init=False) + groups: list = field(default_factory=lambda: [], init=False) + non_hours: set = field(default_factory=lambda: [], init=False) + demand_hours: set = field(default_factory=lambda: {}, init=False) + + non_hours_raw: list[int] = field(default_factory=lambda: [], repr=False, compare=False) + demand_hours_raw: list[int] = field(default_factory=lambda: [], repr=False, compare=False) + + latest_calculation: datetime = field(default=None, init=False) + should_update: bool = field(default=True, init=False) + @property def cold_limit(self) -> datetime: if self.is_cold: @@ -102,22 +109,33 @@ def price_dt(self) -> dict[datetime, float]: return ret def init_vars(self, temp, temp_trend, target_temp, prices_today: list, prices_tomorrow: list, preset: HvacPresets, - min_price: float, non_hours: list = None, high_demand_hours: dict = None, now_dt=None, - latest_boost: datetime = None) -> None: - if non_hours is None: - non_hours = [] - if high_demand_hours is None: - high_demand_hours = [] - self.min_price = min_price - self.prices = prices_today + prices_tomorrow + now_dt=None, latest_boost: datetime = None) -> None: + new_prices = prices_today + prices_tomorrow + new_non_hours = self._set_hours(self.non_hours_raw) + new_demand_hours = self._set_hours(self.demand_hours_raw) + new_temp_trend = DEFAULT_TEMP_TREND if temp_trend > DEFAULT_TEMP_TREND else temp_trend + + if any([ + self.prices != new_prices, + self.latest_boost != latest_boost, + self.non_hours != new_non_hours, + self.demand_hours != new_demand_hours, + self.preset != preset, + self.temp_trend != new_temp_trend, + self.current_temp != temp, + self.target_temp != target_temp + ]): + self.should_update = True + + self.prices = new_prices self.set_now_dt(now_dt) self.latest_boost = latest_boost - self.non_hours = self._set_hours(non_hours) - self.demand_hours = self._set_hours(high_demand_hours) + self.non_hours = new_non_hours + self.demand_hours = new_demand_hours self.set_floating_mean() self._group_prices(prices_today, prices_tomorrow) self.preset = preset - self.temp_trend = DEFAULT_TEMP_TREND if temp_trend > DEFAULT_TEMP_TREND else temp_trend + self.temp_trend = new_temp_trend self.current_temp = temp self.target_temp = target_temp diff --git a/custom_components/peaqhvac/service/hvac/water_heater/water_heater_coordinator.py b/custom_components/peaqhvac/service/hvac/water_heater/water_heater_coordinator.py index 82681d98..3b2e1b5b 100644 --- a/custom_components/peaqhvac/service/hvac/water_heater/water_heater_coordinator.py +++ b/custom_components/peaqhvac/service/hvac/water_heater/water_heater_coordinator.py @@ -36,7 +36,12 @@ def __init__(self, hvac, hub): max_age=3600, max_samples=50, precision=1, ignore=0, outlier=20 ) self.model = WaterBoosterModel(self._hub.state_machine) - self.booster = NextWaterBoost() + self.booster = NextWaterBoost( + min_price = self._hub.sensors.peaqev_facade.min_price, + non_hours = self._hub.options.heating_options.non_hours_water_boost, + demand_hours = self._hub.options.heating_options.demand_hours_water_boost + ) + self._hub.observer.add(ObserverTypes.OffsetsChanged, self._update_operation) self._hub.observer.add("water boost done", self.async_reset_water_boost) async_track_time_interval( @@ -119,13 +124,10 @@ def _get_next_start(self, target_temp: int) -> tuple[datetime, int|None]: ret, override_demand = self.booster.next_predicted_demand( prices_today=self._hub.spotprice.model.prices, prices_tomorrow=self._hub.spotprice.model.prices_tomorrow, - min_price=self._hub.sensors.peaqev_facade.min_price, preset=preset, temp=self.current_temperature, temp_trend=self.temp_trend.gradient_raw, target_temp=target_temp, - non_hours=self._hub.options.heating_options.non_hours_water_boost, - high_demand_hours=self._hub.options.heating_options.demand_hours_water_boost, latest_boost=datetime.fromtimestamp(self.model.latest_boost_call), ) if ret != self.model.next_water_heater_start: diff --git a/custom_components/peaqhvac/service/hvac/water_heater/water_heater_next_start.py b/custom_components/peaqhvac/service/hvac/water_heater/water_heater_next_start.py index 6e27cb2e..a78a88d2 100644 --- a/custom_components/peaqhvac/service/hvac/water_heater/water_heater_next_start.py +++ b/custom_components/peaqhvac/service/hvac/water_heater/water_heater_next_start.py @@ -13,31 +13,31 @@ class NextWaterBoost: - def __init__(self): - self.model = NextWaterBoostModel() - + def __init__(self, min_price: float = None, non_hours: list[int] = None, demand_hours: list[int] = None): + self.model = NextWaterBoostModel(min_price=min_price, non_hours_raw=non_hours, demand_hours_raw=demand_hours) def next_predicted_demand( self, prices_today: list, prices_tomorrow: list, - min_price: float, temp: float, temp_trend: float, target_temp: float, preset: HvacPresets = HvacPresets.Normal, now_dt=None, - non_hours=None, - high_demand_hours=None, latest_boost: datetime = None, ) -> tuple[datetime, int | None]: if len(prices_today) < 1: return datetime.max, None - self.model.init_vars(temp, temp_trend, target_temp, prices_today, prices_tomorrow, preset, min_price, non_hours, - high_demand_hours, now_dt, latest_boost) + self.model.init_vars(temp, temp_trend, target_temp, prices_today, prices_tomorrow, preset, now_dt, latest_boost) - return self._get_next_start( - delay_dt=None if self.model.cold_limit == now_dt else self.model.cold_limit - ) + next_start = self.model.latest_calculation + if self.model.should_update: + next_start = self._get_next_start( + delay_dt=None if self.model.cold_limit == now_dt else self.model.cold_limit + ) + self.model.latest_calculation = next_start + self.model.should_update = False + return next_start def _get_next_start(self, delay_dt=None) -> tuple[datetime, int | None]: last_known = self._last_known_price() @@ -67,7 +67,7 @@ def _get_next_start(self, delay_dt=None) -> tuple[datetime, int | None]: new_demand=self.model.get_demand_minutes(expected_temp) ), None - def _check_intersecting(self, next_dt, last_known) -> datetime | None: + def _check_intersecting(self, next_dt, last_known) -> tuple[datetime, int | None]: intersecting_non_hours = self._intersecting_special_hours(self.model.non_hours, min(next_dt, last_known)) intersecting_demand_hours = self._intersecting_special_hours(self.model.demand_hours, min(next_dt, last_known)) if intersecting_demand_hours: