Home Assistant · price and CO2
Two equally supported ways to get price and CO2 data into Home Assistant
There are two fully supported paths: a native HACS integration with UI-based setup, or a plain REST sensor you paste into YAML. Both use the same API and return current values, live window state and the most useful time windows.
- HACS integration: UI setup, its own entities, currently a custom repository (not yet listed in the HACS default store)
- REST sensor: one endpoint for price, CO2 and useful time windows, no HACS required
- clear separation between base price, CO2 and total-price support - identical on both paths
Which path fits you?
Both paths call the same summary endpoint and return the same data. The difference is setup and maintenance.
Open-source repository for the HACS integration: github.com/BackupBaTTerY/energypriceforecast-home-assistant. Both paths can run side by side, as long as you don't create duplicate entity IDs.
The Home Assistant setup remains available without an API key for up to 48 hours. If you want to plan larger loads across several days, you can try Private Pro with up to 120 hours free for 14 days.
New integrations should now use https://api.energypriceforecast.eu/api/v1/.... Some older project URLs still work for existing setups, but they should no longer be treated as the reference path for new integrations.
Setting up the HACS integration
While the integration isn't in the HACS default store yet, add the repository manually once.
- Open HACS, then the "⋮" menu → Custom repositories.
- Add repository URL
https://github.com/BackupBaTTerY/energypriceforecast-home-assistant, category Integration, add it. - Search for "Energy Price Forecast EU", download it, then restart Home Assistant.
- Settings → Devices & Services → Add Integration, search for "Energy Price Forecast EU" and pick your market plus any optional settings.
The button above ("Add the HACS integration") opens steps 1 and 2 automatically if HACS is already installed.
Compare the forecast with published prices
Green shows published day-ahead prices, orange the current forecast and the dashed blue line the first saved forecast. When official prices arrive, the earlier prediction remains visible for comparison.
- Update Energy Price Forecast EU in HACS to at least 1.12.0 and restart Home Assistant. Also install ApexCharts Card 2.1.0 or newer through HACS.
- Replace all three
sensor.CHANGE_MEplaceholders with the same price-series entity from Developer Tools → States. For your configured all-in price, use the Current retail price entity instead. Paste the YAML into Dashboard → Edit → Add card → Manual.
Collection starts with this update and survives restarts. Snapshots remain until the target market day ends; earlier predictions cannot be reconstructed. captured_at is the Home Assistant observation time, not the model run time. This is not a fixed-lead-time quality measurement or an unlimited archive.
Match the axis label to the sensor unit, e.g. EUR/kWh rather than ct/kWh. A calculated retail price is not a guaranteed bill. Anonymous API access remains up to 48 hours; for up to 120 hours, use the appropriate access and graph_span: 6d. This card is for the HACS integration, not the REST-summary example below, and adds no API requests.
Ready-to-paste YAML
type: custom:apexcharts-card
header:
show: true
title: Energy Price Forecast EU
graph_span: 3d
span:
start: day
now:
show: true
label: Now
yaxis:
- id: price
decimals: 3
apex_config:
title:
text: EUR/kWh
all_series_config:
yaxis_id: price
type: line
curve: stepline
extend_to: false
stroke_width: 2
float_precision: 4
show:
legend_value: false
series:
# Replace ALL three placeholders with the SAME price or retail sensor.
- entity: sensor.CHANGE_ME
name: Known (day-ahead)
color: "#43a047"
data_generator: |
const raw = [...(entity.attributes.raw_today ?? []),
...(entity.attributes.raw_tomorrow ?? [])];
const slots = raw.map(e => ({
start: Date.parse(e.start), end: Date.parse(e.end), value: e.value
})).filter(e => Number.isFinite(e.start) && Number.isFinite(e.end)
&& e.end > e.start && typeof e.value === 'number' && Number.isFinite(e.value))
.sort((a, b) => a.start - b.start);
const points = [];
let previousEnd = null;
for (const slot of slots) {
if (previousEnd !== null && slot.start > previousEnd) {
points.push([previousEnd, null], [slot.start - 1, null]);
}
points.push([slot.start, slot.value], [slot.end - 1, slot.value]);
previousEnd = slot.end;
}
return points;
- entity: sensor.CHANGE_ME
name: Current forecast
color: "#fb8c00"
data_generator: |
const raw = entity.attributes.raw_forecast ?? [];
const slots = raw.map(e => ({
start: Date.parse(e.start), end: Date.parse(e.end), value: e.value
})).filter(e => Number.isFinite(e.start) && Number.isFinite(e.end)
&& e.end > e.start && typeof e.value === 'number' && Number.isFinite(e.value))
.sort((a, b) => a.start - b.start);
const points = [];
let previousEnd = null;
for (const slot of slots) {
if (previousEnd !== null && slot.start > previousEnd) {
points.push([previousEnd, null], [slot.start - 1, null]);
}
points.push([slot.start, slot.value], [slot.end - 1, slot.value]);
previousEnd = slot.end;
}
return points;
- entity: sensor.CHANGE_ME
name: Saved forecast (first seen)
color: "#1e88e5"
stroke_dash: 5
data_generator: |
const raw = entity.attributes.raw_forecast_reference ?? [];
const slots = raw.map(e => ({
start: Date.parse(e.start), end: Date.parse(e.end), value: e.value
})).filter(e => Number.isFinite(e.start) && Number.isFinite(e.end)
&& e.end > e.start && typeof e.value === 'number' && Number.isFinite(e.value))
.sort((a, b) => a.start - b.start);
const points = [];
let previousEnd = null;
for (const slot of slots) {
if (previousEnd !== null && slot.start > previousEnd) {
points.push([previousEnd, null], [slot.start - 1, null]);
}
points.push([slot.start, slot.value], [slot.end - 1, slot.value]);
previousEnd = slot.end;
}
return points;
Optional: tailor the card with AI
Help me tailor an ApexCharts card for Energy Price Forecast EU. First ask for my exact entity ID, sensor unit, integration version, hourly/quarter-hourly settlement and whether I want market or retail prices. Do not ask for an API key.
From integration 1.12.0, Price forecast series and Current retail price expose:
- raw_today and raw_tomorrow: published day-ahead slots.
- raw_forecast: current forecast slots beyond official coverage.
These lists contain {start, end, value}.
- raw_forecast_reference: {start, end, value, captured_at}; the first forecast received for a then-future slot, saved locally. Later forecasts and official prices never overwrite it.
References accumulate only after updating, survive restarts and are removed after the target market day ends. captured_at is the HA observation time, not model creation.
Use green for official prices, orange for the current forecast and dashed blue for the saved forecast, on the same axis in the sensor's unit. Require ApexCharts Card 2.1.0 or newer. Use each series' data_generator and extend_to: false. Draw start through end, preserve gaps with null, and retain genuine zero/negative prices. Never copy official values into a forecast line. Use only the documented attributes.
Optionally ask about the hourly/weekend plan: its hours attribute contains {start, end, average_value}. Mark those times separately without changing the plan. Return complete YAML for a manual Lovelace card and explain where to paste it. A chart alone does not switch a charger.
Recommended endpoint
For Home Assistant, /api/v1/home-assistant/summary is the simplest entry point. It returns not just raw values, but already computed windows for cheapest, greenest and combined use.
https://api.energypriceforecast.eu/api/v1/home-assistant/summary?country=de&hours=48&window_hours=4flat.current_price and flat.cheapest_window_*.flat.current_co2_g_kwh and flat.greenest_window_*.flat.combined_window_* for the price/CO2 compromise.Quick start for copy and paste (REST sensor path)
If you picked the REST sensor path, this is intentionally simple: paste the YAML block, adjust the market, reload Home Assistant.
- Paste the YAML block below into
configuration.yamlor into a package file that Home Assistant already loads. - Only change the
countryparameter. For Denmark, always usedk1ordk2. - Reload or restart Home Assistant.
- Check Developer Tools to confirm that the new sensors return values.
flat fields instead of complex templates, so it stays copy-paste friendly.country. The rest can stay unchanged.REST sensor example
This example polls Germany every 15 minutes. For other markets, only the country parameter changes.
rest:
- resource: "https://api.energypriceforecast.eu/api/v1/home-assistant/summary?country=de&hours=48&window_hours=4"
scan_interval: 900
sensor:
- name: "EnergyPriceForecast current price"
value_template: "{{ value_json.flat.current_price }}"
unit_of_measurement: "EUR/kWh"
- name: "EnergyPriceForecast current CO2 intensity"
value_template: "{{ value_json.flat.current_co2_g_kwh }}"
unit_of_measurement: "gCO2/kWh"
- name: "EnergyPriceForecast cheapest window start"
value_template: "{{ value_json.flat.cheapest_window_start }}"
- name: "EnergyPriceForecast greenest window start"
value_template: "{{ value_json.flat.greenest_window_start }}"
After adding the block, reload or restart Home Assistant and verify the sensor values in Developer Tools.
Official field reference for home-assistant/summary
The summary endpoint is intentionally compact. For many automations, the flat block is already enough. If you want more control or custom dashboards, use the more structured price, co2, combined and source blocks as well.
| Field | Meaning | Typical use |
|---|---|---|
flat.current_price | Current price in EUR/kWh. For the current slot it automatically uses the best available public source. | Simple price sensor for automations. |
flat.current_price_source | Source label of the current price, for example day_ahead. | Check whether the value is already official or still forecast-based. |
flat.current_co2_g_kwh | Current CO2 intensity in gCO2/kWh. | CO2-based shifting of flexible loads. |
flat.cheapest_window_* | Start, end and average value of the cheapest time window in the requested horizon. | EV charging, boilers, heat pumps. |
flat.greenest_window_* | Start, end and average value of the lowest-CO2 time window. | Low-CO2 scheduling. |
flat.combined_window_* | Compromise window between price and CO2. | When both matter and you do not want to define your own weighting. |
price.current | Object with start, end, value, unit and source for the current price slot. | Dashboards and more precise state logic. |
price.cheapest_window_next_horizon | Structured cheapest-window block including duration and source counts. | When you want start, end and context together. |
co2.current | Object with the current CO2 value and its time window. | CO2 display without parsing flat. |
co2.greenest_window_next_horizon | Structured greenest-window block. | Low-CO2 charging or heating logic. |
combined.best_window_next_horizon | Combined recommendation path with price, CO2 and score values. | A single "best now" decision without building your own score. |
source.price | Metadata about the price source, including Firestore document, day-ahead provider and counts of available entries. | Debugging, monitoring and quality checks. |
source.co2 | Metadata about the CO2 source. | Debugging and traceability. |
hourly-forecast is the better endpoint.flat.current_price is the currently usable public price for the running slot. It can come from the official day-ahead auction or from another released source when the full market result is not available yet.retail-forecast endpoint is currently intended only for selected markets. For other markets, the summary deliberately stays on market or base values.When is summary enough, and when do you need hourly-forecast?
summary is enough for simple automations
If you only need the current price, the current CO2 intensity or the best 4-hour window, home-assistant/summary is the right endpoint. It keeps the YAML short and avoids unnecessary parsing.
hourly-forecast is better for charts and complex templates
If you want to draw your own charts, compute your own scores or separate day-ahead values from model forecast values, use hourly-forecast. That endpoint exposes the hourly series more explicitly.
Public API support matrix
Not every visible market is publicly enabled for every endpoint yet. This matrix makes the current support level explicit instead of relying on guesswork.
| Market | hourly-forecast |
home-assistant/summary |
co2-live |
Retail end-price forecastretail-forecast |
|---|---|---|---|---|
| DE | yes | yes | yes | yes |
| NL | yes | yes | yes | yes |
| BE | yes | yes | yes | no |
| FR | yes | yes | yes | no |
| CZ | yes | yes | yes | no |
| AT | yes | yes | yes | yes |
| PL | yes | yes | yes | no |
| FI | yes | yes | yes | no |
| SE1 | yes | yes | yes | no |
| SE2 | yes | yes | yes | no |
| SE3 | yes | yes | yes | no |
| SE4 | yes | yes | yes | no |
| DK1 | yes | yes | yes | yes |
| DK2 | yes | yes | yes | yes |
| NO1 | yes | yes | yes | yes |
| NO2 | yes | yes | yes | yes |
| NO3 | yes | yes | yes | yes |
| NO4 | yes | yes | yes | yes |
| NO5 | yes | yes | yes | yes |
dk1 or dk2. A generic dk request resolves to DK1.retail-forecast means an estimated end-customer electricity price, not just the wholesale or base market price. It is currently public for DE, NL, DK1, DK2, AT, and NO1 to NO5.Adapt it to your setup with AI
This prompt helps an AI turn the summary endpoint into a Home Assistant automation for your actual load, deadline and safety rules. It supplements the copy-paste setup above rather than replacing it.
Never give an AI your real API key. Keep YOUR_API_KEY in the generated result and insert the key locally in Home Assistant.
Show the copyable prompt
Help me build a safe Home Assistant automation using the Energy Price Forecast EU API.
Read the API contract first:
https://energypriceforecast.eu/openapi/integration-api.json
Use /api/v1/home-assistant/summary and do not invent fields. The API uses official day-ahead prices wherever available and forecast values only for the remaining open horizon.
Before writing YAML, ask me no more than these questions, one at a time:
1. Country or bidding zone?
2. Which load should be controlled and which Home Assistant entity switches it?
3. Power, required runtime and latest completion time?
4. Optimise for price, CO2 or both?
5. Free without a key and up to 48 hours, or Private Pro with up to 120 hours?
6. Do I use packages or configuration.yaml?
Rules for your result:
- Free: hours=48 and no Authorization header.
- Private Pro: hours=120 and header Authorization: Bearer YOUR_API_KEY.
- Never ask me to paste the real key into the chat.
- Handle HTTP errors and check meta.api_key_state and meta.allowed_horizon_hours.
- Briefly explain every API property you use.
- Produce complete, correctly indented YAML and state exactly where it belongs.
- Start with a safe test mode using a notification or input_boolean. Put real load switching in a separate, clearly marked step.
- Define safe behaviour for stale, missing or implausible data. An API error must never switch the load on unexpectedly.
- Finish with a checklist: HTTP 200, sensors populated, allowed horizon correct and test mode triggered correctly.
Ask when information is missing. Do not silently assume entity names, tariffs or electrical limits.
Machine-readable foundation: OpenAPI contract for the integration endpoints.
Important notes
Is there an official Home Assistant integration?
Yes, a native HACS integration is available (currently as a custom repository, see the comparison above). This page's REST-sensor path remains fully supported too - it's easier to test, easier to reason about and already sufficient for many setups.
Are the API prices my exact household electricity price?
Not automatically. hourly-forecast and the Home Assistant summary start with market/base prices. The exact total price depends on country-specific grid fees, taxes, levies and supplier markup.
What happens for an unsupported country?
The public API now returns an explicit error instead of silently falling back to Germany. That is intentional so automations do not run on wrong data.
What if no values appear after pasting the YAML?
The most common causes are: the YAML was pasted into the wrong place, Home Assistant was not reloaded, the country value is unsupported, or the YAML indentation is broken. Developer Tools and the Home Assistant logs usually show this immediately.