Get electricity price forecasts into your smart home and charge your EV automatically when power is cheap.
Create an account
Sign up for free, no payment details required.
Create an API key
Generate a token under API Keys.
Pick your platform
Copy the matching code block below, paste it, done.
YOUR_API_TOKEN. Once you are logged in and have created a key, we insert it on copy without ever showing it on the page: API Keys
For both options, data is delivered from midnight of the current day. Forecasts are updated twice daily.
The fastest route is a REST sensor in your configuration.yaml. No HACS, no extra integration.
Home Assistant already ships with everything you need. The built-in RESTful integration fetches the forecast every six hours, and template entities derive the values you automate on to the minute. Nothing extra to install.
1. Add the REST sensor
Add the following block to your configuration.yaml. The sensor stores the full quarter hourly forecast in the data attribute. Its state is when we calculated the forecast, not the price.
rest:
- resource: "https://www.energyforecast.de/api/v2/forecast?token=YOUR_API_TOKEN"
scan_interval: 21600
timeout: 30
sensor:
- name: "Energyforecast Forecast"
unique_id: energyforecast_forecast
value_template: "{{ value_json.generated_at }}"
device_class: timestamp
json_attributes:
- data
- valid_until
- plan
Six hours is plenty because we recalculate the forecast twice a day. That is four requests per day, so your daily limit stays practically untouched.
2. Turn it into usable entities
These template entities read the stored forecast and compare it against the current time. Because they use now(), Home Assistant recalculates them every minute even though the data is only fetched every six hours.
template:
- sensor:
# Current total price incl. fixed costs, grid fees and taxes
- name: "Energyforecast Price Now"
unique_id: energyforecast_price_now
unit_of_measurement: "ct/kWh"
state_class: measurement
availability: "{{ state_attr('sensor.energyforecast_forecast', 'data') is not none }}"
state: >
{% set slots = state_attr('sensor.energyforecast_forecast', 'data') or [] %}
{% set ns = namespace(price='') %}
{% for s in slots if ns.price == '' and as_timestamp(s.end) > as_timestamp(now()) %}
{% set ns.price = s.total_ct_kwh | round(2) %}
{% endfor %}
{{ ns.price }}
# Start of the cheapest contiguous 4 hour block (16 quarter hours)
- name: "Energyforecast Best Charging Start"
unique_id: energyforecast_best_charging_start
device_class: timestamp
availability: "{{ state_attr('sensor.energyforecast_forecast', 'data') is not none }}"
state: >
{% set size = 16 %}
{% set slots = state_attr('sensor.energyforecast_forecast', 'data') or [] %}
{% set ns = namespace(future=[]) %}
{% for s in slots if as_timestamp(s.end) > as_timestamp(now()) %}
{% set ns.future = ns.future + [s] %}
{% endfor %}
{% set window = ns.future[:96] %}
{% set best = namespace(price=none, start='') %}
{% for i in range(0, [(window | count) - size + 1, 0] | max) %}
{% set block = window[i:i + size] %}
{% set avg = (block | map(attribute='total_ct_kwh') | sum) / size %}
{% if best.price is none or avg < best.price %}
{% set best.price = avg %}
{% set best.start = block[0].start %}
{% endif %}
{% endfor %}
{{ best.start }}
- binary_sensor:
# On when the current quarter hour is among the 16 cheapest of the next 24 h
- name: "Energyforecast Cheap Now"
unique_id: energyforecast_cheap_now
icon: mdi:ev-station
state: >
{% set size = 16 %}
{% set slots = state_attr('sensor.energyforecast_forecast', 'data') or [] %}
{% set ns = namespace(future=[]) %}
{% for s in slots if as_timestamp(s.end) > as_timestamp(now()) %}
{% set ns.future = ns.future + [s] %}
{% endfor %}
{% set window = ns.future[:96] %}
{% set cheapest = (window | sort(attribute='total_ct_kwh'))[:size] | map(attribute='start') | list %}
{{ (window | count) > 0 and window[0].start in cheapest }}
This gives you:
sensor.energyforecast_forecast – the full quarter hourly forecast in the data attribute, its state is when we calculated itsensor.energyforecast_price_now – current total price in ct/kWh, small enough to keep in historysensor.energyforecast_best_charging_start – start of the cheapest contiguous 4 hour block within the next 24 hoursbinary_sensor.energyforecast_cheap_now – on whenever the current quarter hour is among the 16 cheapest of the next 24 hours3. Keep your database small (recommended)
Depending on your plan the forecast sensor carries a few tens to over a hundred kilobytes as an attribute. Exclude it from the recorder so your database does not grow unnecessarily. The small derived sensors are still recorded.
recorder:
exclude:
entities:
- sensor.energyforecast_forecast
4. Restart Home Assistant
Check the configuration under Developer tools, YAML, Check configuration, then restart. Afterwards you find the entities under Settings, Devices and services, Entities.
This automation switches your wallbox on as soon as the current hour is among the cheapest, and off again when it is not. Replace switch.wallbox with your own entity.
alias: Charge EV during the cheapest hours
description: Switches the wallbox on during the 4 cheapest hours of the next 24 hours
triggers:
- trigger: state
entity_id: binary_sensor.energyforecast_cheap_now
actions:
- choose:
- conditions:
- condition: state
entity_id: binary_sensor.energyforecast_cheap_now
state: "on"
sequence:
# Power is cheap right now, start charging
- action: switch.turn_on
target:
entity_id: switch.wallbox
default:
# Power is expensive right now, pause charging
- action: switch.turn_off
target:
entity_id: switch.wallbox
mode: single
Add it via Settings, Automations and scenes, Create automation, three dot menu, Edit in YAML.
With the ApexCharts Card (via HACS) you can render the full price curve as a bar chart.
type: custom:apexcharts-card
graph_span: 72h
span:
start: day
experimental:
color_threshold: true
header:
show: true
show_states: true
colorize_states: true
title: Electricity price forecast
now:
show: true
color: "#ef4444"
label: Now
yaxis:
- id: price
min: ~0
decimals: 1
apex_config:
title:
text: ct/kWh
apex_config:
chart:
height: 280
legend:
show: false
xaxis:
labels:
format: "ddd HH:mm"
tooltip:
x:
format: "ddd dd.MM. HH:mm"
series:
# Price curve - green up to 25 ct, yellow up to 35 ct, red above
- entity: sensor.energyforecast_forecast
name: Price
type: column
yaxis_id: price
unit: " ct/kWh"
float_precision: 2
show:
in_header: false
extremas: true
color_threshold:
- value: 0
color: "#16a34a"
- value: 25
color: "#eab308"
- value: 35
color: "#ef4444"
data_generator: |
return (entity.attributes.data || []).map(slot => {
return [new Date(slot.start).getTime(), slot.total_ct_kwh];
});
# Card header - price now, today's average and the cheapest quarter hour of the next 24 hours
- entity: sensor.energyforecast_forecast
name: Now
yaxis_id: price
unit: " ct/kWh"
float_precision: 2
show:
in_chart: false
in_header: true
data_generator: |
const now = Date.now();
const current = (entity.attributes.data || [])
.find(slot => new Date(slot.end).getTime() > now);
return current ? [[now, current.total_ct_kwh]] : [];
- entity: sensor.energyforecast_forecast
name: Ø today
yaxis_id: price
unit: " ct/kWh"
float_precision: 1
show:
in_chart: false
in_header: true
data_generator: |
const today = new Date().toDateString();
const slots = (entity.attributes.data || [])
.filter(slot => new Date(slot.start).toDateString() === today);
if (slots.length === 0) return [];
const avg = slots.reduce((sum, slot) => sum + slot.total_ct_kwh, 0) / slots.length;
return [[Date.now(), avg]];
- entity: sensor.energyforecast_forecast
name: Min 24 h
yaxis_id: price
unit: " ct/kWh"
float_precision: 1
show:
in_chart: false
in_header: true
data_generator: |
const now = Date.now();
const slots = (entity.attributes.data || [])
.filter(slot => new Date(slot.end).getTime() > now)
.slice(0, 96);
if (slots.length === 0) return [];
return [[now, Math.min(...slots.map(slot => slot.total_ct_kwh))]];
The card covers 3 days - exactly the range your plan delivers. The vertical line marks the current quarter hour, bars are coloured by price level, the high and low point are labelled, and price now, today's average and the minimum of the next 24 hours sit above the chart. All values come from the REST sensor, so the card works without the template entities.
In Home Assistant you can integrate the forecasts via the EPEX Spot plugin. Depending on whether you use the Free or Pro plan, you get 2 or 6 full days of forecast.
Here's how:
If you already run evcc, the setup is a handful of lines in your evcc.yaml.
The forecasts can be integrated into evcc to control when your car is charged.
Here's how:
The price is then calculated using the formula <code class="bg-gray-50 px-2 py-1 rounded text-sm font-mono">(Preis + fixed_cost_cent) * (1 + vat / 100.0)</code>.
tariffs:
grid:
type: template
template: energyforecast
token: YOUR_API_TOKEN
charges: # Additional fixed surcharge per kWh (e.g. 0.05 for 5 cents) (optional)
tax: # Tax, additional percentage surcharge (e.g. 0.2 for 20%) (optional)
Before integration, only EPEX Spot day-ahead prices are available.
After integration
Fetch the forecast with the JavaScript adapter and expose it as data points.
ioBroker is an open-source IoT platform that is especially popular in Germany. The JavaScript adapter fetches the forecast and exposes it as data points.
Here's how:
// Create the data points (only needed on the first run)
createState("javascript.0.energyforecast.data", "[]", { type: "string" });
createState("javascript.0.energyforecast.price_now", 0, { type: "number", unit: "ct/kWh" });
createState("javascript.0.energyforecast.cheap_now", false, { type: "boolean" });
// Fetch and cache the forecast (every 6 hours)
async function fetchForecast() {
const res = await httpGetAsync("https://www.energyforecast.de/api/v2/forecast?token=YOUR_API_TOKEN");
setState("javascript.0.energyforecast.data", JSON.stringify(JSON.parse(res.data).data), true);
}
// Determine the current quarter hour from the cached data (every 15 minutes)
function evaluate() {
const slots = JSON.parse(getState("javascript.0.energyforecast.data").val || "[]");
const now = Date.now();
const upcoming = slots.filter(s => new Date(s.end).getTime() > now).slice(0, 96);
if (upcoming.length === 0) return;
const cheapest = [...upcoming]
.sort((a, b) => a.total_ct_kwh - b.total_ct_kwh)
.slice(0, 16)
.map(s => s.start);
setState("javascript.0.energyforecast.price_now", upcoming[0].total_ct_kwh, true);
setState("javascript.0.energyforecast.cheap_now", cheapest.includes(upcoming[0].start), true);
}
schedule("0 */6 * * *", async () => { await fetchForecast(); evaluate(); });
schedule("*/15 * * * *", evaluate);
fetchForecast().then(evaluate);
Tip: Use total_ct_kwh for the total price including fixed costs, grid fees and taxes. price_ct_kwh holds the plain market price if you want to do your own maths.
Flow based automation, standalone or as a Home Assistant add-on.
The flow separates fetching from evaluating: one tick pulls the forecast every six hours and stores it in the flow context, a second one evaluates it every 15 minutes and decides whether to charge right now.
Here's how:
[
{
"id": "ef_fetch_tick",
"type": "inject",
"name": "Every 6 hours",
"repeat": "21600",
"once": true,
"onceDelay": "5",
"wires": [["ef_fetch"]]
},
{
"id": "ef_fetch",
"type": "http request",
"name": "Energyforecast",
"method": "GET",
"ret": "obj",
"url": "https://www.energyforecast.de/api/v2/forecast?token=YOUR_API_TOKEN",
"wires": [["ef_store"]]
},
{
"id": "ef_store",
"type": "function",
"name": "Cache the forecast",
"func": "flow.set('energyforecast', msg.payload.data);\nreturn null;",
"outputs": 1,
"wires": [[]]
},
{
"id": "ef_eval_tick",
"type": "inject",
"name": "Every 15 minutes",
"repeat": "900",
"wires": [["ef_eval"]]
},
{
"id": "ef_eval",
"type": "function",
"name": "Cheapest quarter hours",
"func": "const size = 16;\nconst slots = flow.get('energyforecast') || [];\nconst now = Date.now();\nconst upcoming = slots.filter(s => new Date(s.end).getTime() > now).slice(0, 96);\nif (upcoming.length === 0) { return null; }\nconst cheapest = [...upcoming].sort((a, b) => a.total_ct_kwh - b.total_ct_kwh).slice(0, size);\nconst cheapNow = cheapest.some(s => s.start === upcoming[0].start);\nmsg.payload = cheapNow ? 'on' : 'off';\nmsg.priceNow = upcoming[0].total_ct_kwh;\nreturn msg;",
"outputs": 1,
"wires": [[]]
}
]
Afterwards msg.payload holds on or off, and msg.priceNow holds the current total price in ct/kWh.
Fetch the forecast via the HTTP binding and derive the current price in a rule.
openHAB can query our API directly through the HTTP binding, without any extra add-on.
1. Create the thing
Thing http:url:energyforecast "Energyforecast" [
baseURL="https://www.energyforecast.de/api/v2/forecast?token=YOUR_API_TOKEN",
refresh=21600,
timeout=30000
] {
Channels:
Type string : forecast "Forecast" [ stateTransformation="JSONPATH:$.data" ]
}
2. Create the items
String Energyforecast_Forecast "Forecast"
{ channel="http:url:energyforecast:forecast" }
Number Energyforecast_Price_Now "Current price [%.2f ct/kWh]"
Switch Energyforecast_Cheap_Now "Cheap charging window"
3. Add a rule for the current price
The HTTP binding cannot pick a "current" quarter hour via JSONPATH. This rule in your JS Scripting folder evaluates the stored list every 15 minutes and fills the two derived items.
rules.JSRule({
name: "Energyforecast",
triggers: [triggers.GenericCronTrigger("0 */15 * * * ?")],
execute: () => {
const slots = JSON.parse(items.Energyforecast_Forecast.state || "[]");
const now = Date.now();
const upcoming = slots
.filter(s => new Date(s.end).getTime() > now)
.slice(0, 96);
if (upcoming.length === 0) return;
const cheapest = [...upcoming]
.sort((a, b) => a.total_ct_kwh - b.total_ct_kwh)
.slice(0, 16)
.map(s => s.start);
items.Energyforecast_Price_Now.postUpdate(upcoming[0].total_ct_kwh);
items.Energyforecast_Cheap_Now.postUpdate(cheapest.includes(upcoming[0].start) ? "ON" : "OFF");
}
});
Pull the raw data over REST and build it into your own project.
If you know your way around, you can easily retrieve data via the API and use it in your project. The API documentation can be found at Swagger / Open API Documentation
Endpoints
| Endpoint | What for |
|---|---|
/api/v2/forecast
Current
|
Quarter hourly values with the raw price (price_ct_kwh) and the total price including fixed costs, grid fees and taxes (total_ct_kwh), both in cents. Every example on this page uses this endpoint. |
/api/v1/predictions/prices_for_ha
Legacy
|
Hourly values in EUR per kWh plus a ready made current price. Being replaced by v2. |
/api/v1/predictions/prices
Legacy
|
Just the price values as a flat list, handy for simple scripts. Being replaced by v2. |
Parameters
| Parameter | Meaning |
|---|---|
token |
Your API key. Required. |
resolution |
HOURLY or QUARTER_HOURLY. Applies to the v1 endpoints only, v2 always returns quarter hourly values. |
fixed_cost_cent |
Fixed costs in cents per kWh. Overrides the setting from your profile. |
vat |
VAT rate in percent, for example 19. Overrides the setting from your profile. |
market_zone |
Market zone, defaults to DE-LU. |
Try it quickly
curl "https://www.energyforecast.de/api/v2/forecast?token=YOUR_API_TOKEN"
If you build an integration that could be useful to others, let us know. We appreciate every integration and are happy to help spread the word.
Contact