Skip to content

Commit 08f1fb0

Browse files
benderlMartinRinas
andauthored
Feature fixed hour tariff (#2170)
* fixed hour tariffs * consider quater when checking overlapping tariff windows --------- Co-authored-by: MartinRinas <martrin@microsoft.com>
1 parent ad2aa3a commit 08f1fb0

3 files changed

Lines changed: 112 additions & 0 deletions

File tree

packages/modules/electricity_tariffs/fixed_hours/__init__.py

Whitespace-only changes.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Optional, List, Dict
2+
3+
4+
class FixedHoursTariffConfiguration:
5+
def __init__(self, default_price: Optional[float] = None, tariffs: List[Dict[str, any]] = []) -> None:
6+
self.default_price = default_price
7+
self.tariffs = tariffs
8+
'''
9+
Example configuration:
10+
"tariffs": [
11+
{
12+
"name": "high_tariff",
13+
"price": 0.20,
14+
"active_times": {
15+
"dates": [("01-01", "31-03"), ("01-07", "30-09")], # applicable date ranges (day-month)
16+
"times": [("08:00", "12:00"), ("18:00", "22:00")] # active times during the day
17+
}
18+
},
19+
{
20+
"name": "low_tariff",
21+
"price": 0.05,
22+
"active_times": {
23+
"dates": [("01-04", "30-06"), ("01-10", "31-12")], # applicable date ranges (day-month)
24+
"times": [("00:00", "06:00"), ("22:00", "23:59")] # active times during the day
25+
}
26+
}
27+
]
28+
'''
29+
30+
31+
class FixedHoursTariff:
32+
def __init__(self,
33+
name: str = "Feste Tarifstunden (z.b. §14a EnWG Modul3)",
34+
type: str = "fixed_hours",
35+
configuration: FixedHoursTariffConfiguration = None) -> None:
36+
self.name = name
37+
self.type = type
38+
self.configuration = configuration or FixedHoursTariffConfiguration()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env python3
2+
import logging
3+
import datetime
4+
import time
5+
from typing import List, Tuple, Dict
6+
7+
from modules.electricity_tariffs.fixed_hours.config import FixedHoursTariff, FixedHoursTariffConfiguration
8+
from modules.common.abstract_device import DeviceDescriptor
9+
from modules.common.component_state import TariffState
10+
# from modules.common.configurable_tariff import ConfigurableTariff
11+
12+
log = logging.getLogger(__name__)
13+
14+
15+
def to_time(time_str: str) -> datetime.time:
16+
if time_str == "24:00":
17+
return datetime.time(23, 59, 59)
18+
return datetime.datetime.strptime(time_str, "%H:%M").time()
19+
20+
21+
def to_date(date_str: str, time_slot: datetime.datetime) -> datetime.date:
22+
date = datetime.datetime.strptime(date_str, "%d-%m").date().replace(year=datetime.datetime.now().year)
23+
if date.year < time_slot.year: # Beim Jahreswechsel das korrekte Jahr setzen
24+
date = date.replace(year=time_slot.year)
25+
return date
26+
27+
28+
def validate_tariff_times(config: FixedHoursTariffConfiguration) -> None:
29+
time_slots: List[Tuple[datetime.time, datetime.time, List[Tuple[str, str]]]] = []
30+
for tariff in config.tariffs:
31+
for start, end in tariff["active_times"]["times"]:
32+
start_time = to_time(start)
33+
end_time = to_time(end)
34+
for existing_start, existing_end, existing_dates in time_slots:
35+
if (start_time < existing_end and end_time > existing_start and
36+
any(start <= existing_end and end >= existing_start for start, end in existing_dates)):
37+
raise ValueError(f"Overlapping time window detected: {start} - {end} in tariff '{tariff['name']}'")
38+
time_slots.append((start_time, end_time, tariff["active_times"]["dates"]))
39+
40+
41+
def fetch(config: FixedHoursTariffConfiguration) -> TariffState:
42+
validate_tariff_times(config)
43+
44+
current_time = datetime.datetime.now().replace(minute=0, second=0, microsecond=0)
45+
prices: Dict[str, float] = {}
46+
47+
for i in range(24): # get prices for the next 24 hours
48+
time_slot = current_time + datetime.timedelta(hours=i)
49+
epoch_time = int(time.mktime(time_slot.timetuple()))
50+
price = config.default_price / 1000
51+
52+
for tariff in config.tariffs:
53+
active_times = [(to_time(start), to_time(end)) for start, end in tariff["active_times"]["times"]]
54+
active_dates = [
55+
(to_date(start, time_slot), to_date(end, time_slot))
56+
for start, end in tariff["active_times"]["dates"]
57+
]
58+
if (any(start <= time_slot.time() < end for start, end in active_times) and
59+
any(start <= time_slot.date() <= end for start, end in active_dates)):
60+
price = tariff["price"] / 1000
61+
break # Break since we found a matching tariff
62+
63+
prices[str(epoch_time)] = price
64+
65+
return TariffState(prices=prices)
66+
67+
68+
def create_electricity_tariff(config: FixedHoursTariff):
69+
def updater() -> TariffState:
70+
return fetch(config.configuration)
71+
return updater
72+
73+
74+
device_descriptor = DeviceDescriptor(configuration_factory=FixedHoursTariff)

0 commit comments

Comments
 (0)