Skip to content

Commit 4544457

Browse files
authored
Merge pull request #180 from moritzmair/main
Adding Energyforecast.de as a data source
2 parents 410154a + 364add3 commit 4544457

File tree

6 files changed

+125
-2
lines changed

6 files changed

+125
-2
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ You can choose between multiple sources:
2626
4. smartENERGY.at
2727
[smartENERGY.at](https://www.smartenergy.at/api-schnittstellen) provides a free of charge service for their customers. Market price data is available for Austria. So far no user identifiation is required.
2828

29+
5. Energyforecast.de
30+
[Energyforecast.de](https://www.energyforecast.de/api-docs/index.html) provides services to get market price data forecasts for Germany up to 96 hours into the future. An API token is required.
31+
2932
If you like this component, please give it a star on [github](https://github.com/mampfes/hacs_epex_spot).
3033

3134
## Installation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Energyforecast.de"""
2+
3+
from datetime import datetime
4+
import logging
5+
6+
import aiohttp
7+
8+
9+
from ...const import UOM_EUR_PER_KWH
10+
11+
_LOGGER = logging.getLogger(__name__)
12+
13+
14+
class Marketprice:
15+
"""Marketprice class for Energyforecast."""
16+
17+
def __init__(self, data):
18+
self._start_time = datetime.fromisoformat(data["start"])
19+
self._end_time = datetime.fromisoformat(data["end"])
20+
self._price_per_kwh = round(float(data["price"]), 6)
21+
22+
def __repr__(self):
23+
return f"{self.__class__.__name__}(start: {self._start_time.isoformat()}, end: {self._end_time.isoformat()}, marketprice: {self._price_per_kwh} {UOM_EUR_PER_KWH})" # noqa: E501
24+
25+
@property
26+
def start_time(self):
27+
return self._start_time
28+
29+
@property
30+
def end_time(self):
31+
return self._end_time
32+
33+
@property
34+
def price_per_kwh(self):
35+
return self._price_per_kwh
36+
37+
class Energyforecast:
38+
URL = "https://www.energyforecast.de/api/v1/predictions/prices_for_ha"
39+
40+
MARKET_AREAS = ("de",)
41+
42+
def __init__(self, market_area, token: str, session: aiohttp.ClientSession):
43+
self._token = token
44+
self._session = session
45+
self._market_area = market_area
46+
self._marketdata = []
47+
48+
@property
49+
def name(self):
50+
return "Energyforecast API V1"
51+
52+
@property
53+
def market_area(self):
54+
return self._market_area
55+
56+
@property
57+
def duration(self):
58+
return 60
59+
60+
@property
61+
def currency(self):
62+
return "EUR"
63+
64+
@property
65+
def marketdata(self):
66+
return self._marketdata
67+
68+
async def fetch(self):
69+
data = await self._fetch_data(self.URL)
70+
self._marketdata = self._extract_marketdata(data["forecast"]["data"])
71+
72+
async def _fetch_data(self, url):
73+
async with self._session.get(
74+
url, params={"token": self._token, "fixed_cost_cent": 0, "vat": 0}
75+
) as resp:
76+
resp.raise_for_status()
77+
return await resp.json()
78+
79+
def _extract_marketdata(self, data):
80+
return [Marketprice(entry) for entry in data]

custom_components/epex_spot/SourceShell.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
CONF_SOURCE_SMARD_DE,
2323
CONF_SOURCE_SMARTENERGY,
2424
CONF_SOURCE_TIBBER,
25+
CONF_SOURCE_ENERGYFORECAST,
2526
CONF_SURCHARGE_ABS,
2627
CONF_SURCHARGE_PERC,
2728
CONF_TAX,
@@ -31,7 +32,7 @@
3132
DEFAULT_TAX,
3233
EMPTY_EXTREME_PRICE_INTERVAL_RESP,
3334
)
34-
from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, Tibber, smartENERGY
35+
from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, Tibber, smartENERGY, Energyforecast
3536
from .extreme_price_interval import find_extreme_price_interval, get_start_times
3637

3738
_LOGGER = logging.getLogger(__name__)
@@ -68,6 +69,12 @@ def __init__(self, config_entry: ConfigEntry, session: aiohttp.ClientSession):
6869
token=self._config_entry.data[CONF_TOKEN],
6970
session=session,
7071
)
72+
elif config_entry.data[CONF_SOURCE] == CONF_SOURCE_ENERGYFORECAST:
73+
self._source = Energyforecast.Energyforecast(
74+
market_area=config_entry.data[CONF_MARKET_AREA],
75+
token=self._config_entry.data[CONF_TOKEN],
76+
session=session
77+
)
7178

7279
@property
7380
def unique_id(self):

custom_components/epex_spot/config_flow.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
CONF_SOURCE_SMARD_DE,
1717
CONF_SOURCE_SMARTENERGY,
1818
CONF_SOURCE_TIBBER,
19+
CONF_SOURCE_ENERGYFORECAST,
1920
CONF_SURCHARGE_ABS,
2021
CONF_SURCHARGE_PERC,
2122
CONF_TAX,
@@ -26,14 +27,15 @@
2627
DEFAULT_TAX,
2728
DOMAIN,
2829
)
29-
from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, Tibber, smartENERGY
30+
from .EPEXSpot import SMARD, Awattar, EPEXSpotWeb, Tibber, smartENERGY, Energyforecast
3031

3132
CONF_SOURCE_LIST = (
3233
CONF_SOURCE_AWATTAR,
3334
CONF_SOURCE_EPEX_SPOT_WEB,
3435
CONF_SOURCE_SMARD_DE,
3536
CONF_SOURCE_SMARTENERGY,
3637
CONF_SOURCE_TIBBER,
38+
CONF_SOURCE_ENERGYFORECAST,
3739
)
3840

3941

@@ -79,6 +81,15 @@ async def async_step_source(self, user_input=None):
7981
vol.Required(CONF_TOKEN): vol.Coerce(str),
8082
}
8183
)
84+
# Energyforecast API requires a token
85+
elif self._source_name == CONF_SOURCE_ENERGYFORECAST:
86+
areas = Energyforecast.Energyforecast.MARKET_AREAS
87+
data_schema = vol.Schema(
88+
{
89+
vol.Required(CONF_MARKET_AREA): vol.In(sorted(areas)),
90+
vol.Required(CONF_TOKEN): vol.Coerce(str)
91+
}
92+
)
8293
else:
8394
if self._source_name == CONF_SOURCE_AWATTAR:
8495
areas = Awattar.Awattar.MARKET_AREAS

custom_components/epex_spot/const.py

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
CONF_SOURCE_SMARD_DE = "SMARD.de"
2525
CONF_SOURCE_SMARTENERGY = "smartENERGY.at"
2626
CONF_SOURCE_TIBBER = "Tibber"
27+
CONF_SOURCE_ENERGYFORECAST = "Energyforecast.de"
2728

2829
# configuration options for net price calculation
2930
CONF_SURCHARGE_PERC = "percentage_surcharge"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python3
2+
3+
import aiohttp
4+
import asyncio
5+
6+
from .EPEXSpot import Energyforecast
7+
from .const import UOM_EUR_PER_KWH
8+
9+
DEMO_TOKEN = "demo_token" # The "demo_token" token only provides up to 24 hours of forecast data into the future.
10+
11+
async def main():
12+
async with aiohttp.ClientSession() as session:
13+
service = Energyforecast.Energyforecast(market_area="DE-LU", token=DEMO_TOKEN, session=session)
14+
15+
await service.fetch()
16+
print(f"count = {len(service.marketdata)}")
17+
for e in service.marketdata:
18+
print(f"{e.start_time}: {e.price_per_kwh} {UOM_EUR_PER_KWH}")
19+
20+
21+
asyncio.run(main())

0 commit comments

Comments
 (0)