Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions packages/modules/common/configurable_tariff.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime, timedelta
import math
import random
from typing import TypeVar, Generic, Callable
from helpermodules import timecheck
Expand Down Expand Up @@ -64,7 +65,7 @@ def __update_et_provider_data(self) -> tuple[TariffState, int]:
tariff_state = self._remove_outdated_prices(tariff_state, timeslot_length_seconds)
return tariff_state, timeslot_length_seconds

def __calculate_next_query_time(self) -> None:
def _calculate_next_query_time(self, latest_price_timestamp: datetime) -> None:
now = datetime.now()
current_hour = now.hour + 1 # next full hour
log.debug(f"Aktuelle Tarif Update Stunde: {current_hour} Uhr."
Expand All @@ -79,18 +80,28 @@ def __calculate_next_query_time(self) -> None:
# reduce serverload on their site by trying early and randomizing query time minutes and seconds
next_query_time = (now.replace(hour=next_hour, minute=0, second=0, microsecond=0) +
timedelta(days=day_offset, minutes=random.randint(1, 7) * -5))
if latest_price_timestamp < next_query_time:
# Falls die Preise nicht bis zum nächsten geplanten Abruf gültig sind,
# den Abruf auf die Gültigkeitsdauer der Preise setzen
log.debug(f"Die Preise sind nur bis {latest_price_timestamp} gültig, "
f"setze nächsten Abruf auf dieses Datum.")
next_query_time = latest_price_timestamp
Comment on lines +83 to +88
if next_query_time <= now:
next_query_time += timedelta(hours=1)
# Zeit um so viele volle Stunden erhöhen, bis sie in der Zukunft liegt, Minute-Offset bleibt erhalten
delta_seconds = (now - next_query_time).total_seconds()
hours_to_add = max(1, math.floor(delta_seconds / ONE_HOUR_SECONDS) + 1)
next_query_time = next_query_time + timedelta(hours=hours_to_add)
Comment thread
LKuemmel marked this conversation as resolved.
Resolved
self.get.next_query_time = int(next_query_time.timestamp())
log.debug(f"Nächster Abruf der {self.tariff_type} Strompreise geplant für:"
f" {next_query_time} (Unix Timestamp: {self.get.next_query_time})")
Pub().pub(f"openWB/set/optional/ep/{self.tariff_type}/get/next_query_time", int(next_query_time.timestamp()))

def __call_component_updater(self) -> TariffState:
last_known_timestamp = datetime.fromtimestamp(int(max(self.get.prices.keys()) if self.get.prices else 0))
last_known_timestamp = datetime.fromtimestamp(max((int(ts) for ts in self.get.prices.keys()), default=0))
tariff_state = self._component_updater()
if last_known_timestamp < datetime.fromtimestamp(int(max(tariff_state.prices.keys()))):
self.__calculate_next_query_time()
latest_price_timestamp = datetime.fromtimestamp(max((int(ts) for ts in tariff_state.prices.keys()), default=0))
if last_known_timestamp < latest_price_timestamp:
self._calculate_next_query_time(latest_price_timestamp)
return tariff_state

def __log_and_publish_progress(self, timeslot_length_seconds, tariff_state):
Expand Down
60 changes: 56 additions & 4 deletions packages/modules/common/configurable_tariff_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from datetime import datetime, tzinfo
from typing import Optional
from unittest.mock import Mock
from helpermodules import timecheck
import pytest

from modules.common.component_state import TariffState
from modules.common import configurable_tariff
from modules.common.configurable_tariff import ConfigurableFlexibleTariff
from modules.electricity_pricing.flexible_tariffs.awattar.config import AwattarTariff

Expand Down Expand Up @@ -94,9 +97,8 @@ def test_remove_outdated_prices(
monkeypatch.setattr(timecheck, "create_timestamp", Mock(return_value=now))

# test
result = tariff._remove_outdated_prices(
tariff_state, time_slot_seconds[1] - time_slot_seconds[0]
)
# pyright: ignore[reportPrivateUsage]
result = tariff._remove_outdated_prices(tariff_state, time_slot_seconds[1] - time_slot_seconds[0])

# assert
assert result.prices == expected.prices
Expand All @@ -117,4 +119,54 @@ def test_accept_no_prices_at_start(monkeypatch):
monkeypatch.setattr(timecheck, "create_timestamp", Mock(return_value=5))

# test - do not fail
tariff._remove_outdated_prices(TariffState(), 1)
tariff._remove_outdated_prices(TariffState(), 1) # pyright: ignore[reportPrivateUsage]


def test_calculate_next_query_time(monkeypatch: pytest.MonkeyPatch):
# setup
fixed_now = datetime(2022, 5, 16, 10, 10, 0)

class FixedDateTime(datetime):
@classmethod
def now(cls, tz: Optional[tzinfo] = None):
return fixed_now if tz is None else fixed_now.replace(tzinfo=tz)

monkeypatch.setattr(configurable_tariff, "datetime", FixedDateTime)
monkeypatch.setattr(configurable_tariff.random, "randint", Mock(return_value=1))

tariff = ConfigurableFlexibleTariff(AwattarTariff(), Mock(return_value=Mock()))
tariff.tariff_update_hours = [14]

latest_price_timestamp = datetime(2022, 5, 17, 13, 55, 0)

# execution
tariff._calculate_next_query_time(latest_price_timestamp) # pyright: ignore[reportPrivateUsage]

# evaluation
expected_timestamp = int(datetime(2022, 5, 16, 13, 55, 0).timestamp())
assert tariff.get.next_query_time == expected_timestamp


def test_calculate_next_query_time_uses_latest_price_timestamp(monkeypatch: pytest.MonkeyPatch):
# setup
fixed_now = datetime(2022, 5, 16, 10, 10, 0)

class FixedDateTime(datetime):
@classmethod
def now(cls, tz: Optional[tzinfo] = None):
return fixed_now if tz is None else fixed_now.replace(tzinfo=tz)

monkeypatch.setattr(configurable_tariff, "datetime", FixedDateTime)
monkeypatch.setattr(configurable_tariff.random, "randint", Mock(return_value=1))

tariff = ConfigurableFlexibleTariff(AwattarTariff(), Mock(return_value=Mock()))
tariff.tariff_update_hours = [14]

latest_price_timestamp = datetime(2022, 5, 16, 12, 30, 0)

# execution
tariff._calculate_next_query_time(latest_price_timestamp) # pyright: ignore[reportPrivateUsage]

# evaluation
expected_timestamp = int(latest_price_timestamp.timestamp())
assert tariff.get.next_query_time == expected_timestamp
Comment on lines +165 to +172
Loading