Skip to content

Commit

Permalink
#93
Browse files Browse the repository at this point in the history
  • Loading branch information
magnuselden authored and magnuselden committed Nov 30, 2023
1 parent 640c19d commit c33234a
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
HOUR_LIMIT = 18
DELAY_LIMIT = 48
MIN_DEMAND = 26
DEFAULT_TEMP_TREND = -0.4
DEFAULT_TEMP_TREND = -0.5

DEMAND_MINUTES = {
HvacPresets.Normal: {
Expand Down Expand Up @@ -56,8 +56,11 @@ def get_demand(temp) -> Demand:

@dataclass
class NextWaterBoostModel:
prices: list = field(default_factory=lambda: [])
min_price: float = None # type: ignore
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)

prices: list = field(default_factory=lambda: [])

preset: HvacPresets = HvacPresets.Normal
now_dt: datetime = None # type: ignore
Expand All @@ -72,18 +75,20 @@ class NextWaterBoostModel:
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)

def __post_init__(self):
self.now_dt = datetime.now() if self.now_dt is None else self.now_dt
self.non_hours = self._set_hours(self.non_hours_raw)
self.demand_hours = self._set_hours(self.demand_hours_raw)

@property
def cold_limit(self) -> datetime:
if self.is_cold:
return self.now_dt
try:
hourdiff = (self.current_temp - self.target_temp) / self.temp_trend
hourdiff = (self.current_temp - self.target_temp) / -self.temp_trend
except ZeroDivisionError:
hourdiff = DELAY_LIMIT
return self.now_dt + timedelta(hours=hourdiff)
Expand Down Expand Up @@ -111,22 +116,23 @@ def price_dt(self) -> dict[datetime, float]:
def init_vars(self, temp, temp_trend, target_temp, prices_today: list, prices_tomorrow: list, preset: HvacPresets,
now_dt=None, latest_boost: datetime = None) -> None:
self.set_now_dt(now_dt)

new_prices = prices_today + prices_tomorrow
if new_prices != self.prices:
self.prices = new_prices
self.should_update = True
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
]):
]) and not self.should_update:
self.should_update = True

self.prices = new_prices
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ def current_temperature(self) -> float:

@current_temperature.setter
def current_temperature(self, val):
floatval = float(val)
try:
self.temp_trend.add_reading(val=float(val), t=time.time())
if self._current_temp != float(val):
self._current_temp = float(val)
self._check_and_add_trend_reading(floatval)
if self._current_temp != floatval:
self._current_temp = floatval
old_demand = self.demand.value
self.demand = self._current_temp
if self.demand.value != old_demand:
Expand All @@ -91,6 +92,16 @@ def current_temperature(self, val):
_LOGGER.warning(f"unable to set {val} as watertemperature. {E}")
self.model.water_boost.value = False

def _check_and_add_trend_reading(self, val):
raw = self.temp_trend.samples_raw
if len(raw) > 0:
last = raw[-1]
if last[1] != val or time.time() - last[0] > 300:
self.temp_trend.add_reading(val=val, t=time.time())
else:
return
self.temp_trend.add_reading(val=val, t=time.time())

@property
def demand(self) -> Demand:
return self._demand
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def _get_next_start(self, delay_dt=None) -> tuple[datetime, int | None]:
latest_limit = self.model.latest_boost + timedelta(hours=24) if self.model.latest_boost else datetime.now()
if latest_limit < self.model.now_dt and self.model.is_cold:
"""It's been too long since last boost. Boost now."""
_LOGGER.debug(f"next boost now due to it being more than 24h since last time")
return self.model.now_dt.replace(
minute=self._set_minute_start(now_dt=datetime.now()),
second=0,
Expand All @@ -57,7 +58,8 @@ def _get_next_start(self, delay_dt=None) -> tuple[datetime, int | None]:
next_dt = self._calculate_next_start(delay_dt) # todo: must also use latestboost +24h in this.

intersecting1 = self._check_intersecting(next_dt, last_known)
if intersecting1:
if intersecting1 and intersecting1[0] > self.model.cold_limit:
_LOGGER.debug(f"returning next boost based on intersection of hours. original: {next_dt}, inter: {intersecting1}")
return intersecting1

expected_temp = min(self._get_temperature_at_datetime(next_dt), 39)
Expand All @@ -73,7 +75,7 @@ def _check_intersecting(self, next_dt, last_known) -> tuple[datetime, int | None
if intersecting_demand_hours:
best_match = self._get_best_match(intersecting_non_hours, intersecting_demand_hours)
if best_match:
# print(f"best match: {best_match}")
print(f"best match: {best_match}")
expected_temp = min(self._get_temperature_at_datetime(best_match), 39)
ret = self._set_start_dt(
low_period=0,
Expand All @@ -92,7 +94,7 @@ def _get_best_match(non_hours, demand_hours) -> datetime | None:
for hour in range(first_demand.hour - 1, -1, -1):
if hour not in non_hours:
if hour - 1 not in non_hours:
return first_demand.replace(hour=hour - 1)
return first_demand.replace(hour=hour)
return None

def _intersecting_special_hours(self, hourslist, next_dt) -> list[datetime]:
Expand Down Expand Up @@ -166,10 +168,7 @@ def _values_are_good(self, i) -> bool:

def _calculate_next_start(self, delay_dt=None) -> datetime:
check_dt = (delay_dt if delay_dt else self.model.now_dt).replace(minute=0, second=0, microsecond=0)
if self.model.is_cold:
print("is cold")
else:
print("is not cold- will be at:", self.model.cold_limit)
print("is cold") if self.model.is_cold else print("is not cold- will be at:", self.model.cold_limit)
try:
if self.model.prices[check_dt.hour] < self.model.floating_mean and self.model.is_cold and not any(
[
Expand All @@ -196,8 +195,13 @@ def find_lowest_2hr_combination(self, start_index: int, end_index: int) -> int:
current_sum = self.model.prices[i] + self.model.prices[i + 1]
if current_sum < min_sum:
if self._values_are_good(i):
if self.model.is_cold:
print(f"it is cold so i'm returning {i} despite it not being the lowest hour")
return i
elif self.model.cold_limit < (self.model.now_dt.replace(hour=0) + timedelta(hours=i)):
print(f"it is not cold yet but i'm returning {i} despite it not being the lowest hour since i think it will be cold by then")
return i
# todo: should also check so that i is not more than 24hr from latest boost.
# todo: should also check if we would bump into any demand hours before this hour. So we don't forget that.
min_sum = current_sum
min_start_index = i
return min_start_index
Expand Down

0 comments on commit c33234a

Please sign in to comment.