A Home Assistant custom integration for dynamic/real-time energy prices in the Netherlands and Belgium, with a provider-pluggable architecture.
| Provider | API Type | Auth | Electricity | Gas | Belgium |
|---|---|---|---|---|---|
| Essent (NL) | REST | None (header fix) | ✅ | ✅ | ❌ |
| EnergyZero (NL) | REST | None | ✅ | ✅ | ❌ |
| Eneco (NL) | REST (EnergyZero alias) | None | ✅ | ✅ | ❌ |
| Frank Energie (NL/BE) | GraphQL | None | ✅ | ✅ | ✅ |
| Vattenfall (NL) | — | — | ❌ | ❌ | ❌ |
Vattenfall TijdPrijs uses fixed time-of-use tariffs, not a real-time dynamic pricing API — it cannot be implemented as a dynamic price provider.
- Essent — Uses the
essent.nlpublic pricing API. Requires ax-request-origin: clientheader (the official HA integration has been broken since 2026-06-03 because this header was not sent). - EnergyZero — Uses the
api.energyzero.nlpublic REST API. No authentication required. - Eneco — Reuses the EnergyZero public API (white-label backend). Same features, listed separately for clarity.
- Frank Energie — Uses the
graphql.frankenergie.nlpublic GraphQL API. No authentication required. Supports Netherlands (default) and Belgium (addx-country: BEheader).
- Ensure HACS is installed.
- Go to HACS > Integrations > Custom repositories.
- Add
https://github.com/gokhancelik/energy-api-integrationsas category Integration. - Click Download on the "Dynamic Energy Prices" integration.
- Restart Home Assistant.
- Copy
custom_components/dynamic_energy_prices/into your HAcustom_components/directory. - Restart Home Assistant.
- Go to Settings > Devices & Services > Add Integration.
- Search for "Dynamic Energy Prices".
- Select your provider (Essent, EnergyZero, Eneco, or Frank Energie).
- For Frank Energie: optionally toggle the Belgium region.
- After setup, go to Options to configure a custom price threshold for the cheap electricity binary sensor (optional — defaults to today's average).
- The integration will create sensors for current, next, average, lowest, and highest electricity prices, plus gas prices if available.
| Sensor | Description | Enabled by default |
|---|---|---|
current_electricity_price |
Current hourly electricity price | ✅ |
next_electricity_price |
Next upcoming hourly price | ✅ |
average_electricity_price |
Average of all 24 hourly prices today | ✅ |
lowest_electricity_price |
Lowest price today | ❌ |
highest_electricity_price |
Highest price today | ❌ |
cheapest_3h_block_electricity |
Cheapest contiguous 3-hour block (sliding window) | ❌ |
last_updated |
Timestamp of the last successful data refresh | ❌ |
next_update |
Scheduled time of the next data refresh | ❌ |
| Sensor | Description | Enabled by default |
|---|---|---|
current_gas_price |
Current hourly gas price (if available) | ✅ |
next_gas_price |
Next upcoming gas price (if available) | ✅ |
Available for EnergyZero, Eneco, and Frank Energie (providers that
implement async_fetch_prices_for_date). Not created for Essent since it
does not offer a date-based API.
| Sensor | Description | Enabled by default |
|---|---|---|
tomorrow_average_electricity_price |
Average electricity price for tomorrow | ✅ |
tomorrow_lowest_electricity_price |
Lowest electricity price tomorrow | ❌ |
tomorrow_highest_electricity_price |
Highest electricity price tomorrow | ❌ |
tomorrow_average_gas_price |
Average gas price for tomorrow (if available) | ✅ |
tomorrow_lowest_gas_price |
Lowest gas price tomorrow (if available) | ❌ |
tomorrow_highest_gas_price |
Highest gas price tomorrow (if available) | ❌ |
These sensors expose the components that make up the current electricity price.
The current_electricity_market_price sensor can be used as the
Energy Dashboard Export Compensation price entity.
| Sensor | Description |
|---|---|
current_electricity_market_price |
Raw market price component |
current_electricity_supplier_markup |
Supplier surcharge component |
current_electricity_energy_tax |
Energy tax component |
| Sensor | Description | Enabled by default |
|---|---|---|
cheap_electricity |
ON when the current price is below today's average price | ❌ |
The current_electricity_price sensor includes:
price_breakdown—market_price,supplier_markup,energy_taxcomponentshourly_prices— list of{start, end, price}for all hours the provider exposes. Includes yesterday + today + tomorrow for Essent (72 entries) and today + tomorrow for EnergyZero, Eneco, and Frank Energie (48 entries), so automations can schedule loads across midnight. Note thatstartis aHH:MMtime so hour labels repeat across the included days. Useful for custom Lovelace cards or Grafana.
The cheapest_3h_block_electricity sensor includes:
end_time— end time of the cheapest blockaverage_price— average price across the blockprices— list of individual hourly prices in the block
The cheap_electricity binary sensor includes current_price,
average_price, and threshold attributes. The threshold can be set to a
custom value via Configure on the integration entry. When no custom
threshold is set, today's average price is used.
The sensor has device_class: timestamp — its state is the start time as a
datetime. Use it directly in automations:
automation:
- alias: "Run dishwasher at cheapest block"
trigger:
- platform: state
entity_id: sensor.essent_cheapest_3h_block_electricity
action:
- delay:
hours: 0 # fires immediately when cheapest block updates
- service: switch.turn_on
target:
entity_id: switch.dishwasherTo start at the beginning of the block instead of immediately on update:
automation:
- alias: "Start charger at cheapest 3h block"
trigger:
- platform: template
value_template: >
{{ now().hour ==
state_attr('sensor.essent_cheapest_3h_block_electricity', 'start_time')[0:2] | int
and now().minute == 0 }}
action:
- service: switch.turn_on
target:
entity_id: switch.ev_chargertemplate:
- sensor:
- name: "Cheapest block end"
state: >
{{ state_attr('sensor.essent_cheapest_3h_block_electricity', 'end_time') }}
- name: "Cheapest block average price"
unit_of_measurement: "EUR/kWh"
device_class: monetary
state: >
{{ state_attr('sensor.essent_cheapest_3h_block_electricity', 'average_price') }}template:
- sensor:
- name: "Current hour price rank"
state: >
{% set prices = state_attr('sensor.essent_current_electricity_price', 'hourly_prices') %}
{% set sorted = prices | sort(attribute='price') %}
{% for p in sorted %}
{% if p.start == now().strftime('%H:%M') %}
{{ loop.index }} / {{ prices | length }}
{% endif %}
{% endfor %}The current_electricity_market_price breakdown sensor provides the
pre-tax market rate and is the correct entity for the Energy Dashboard
Export Compensation price entity.
With the binary sensor enabled, you can trigger on state changes:
automation:
- alias: "Notify when electricity is cheap"
trigger:
- platform: state
entity_id: binary_sensor.essent_cheap_electricity
to: "on"
action:
- service: persistent_notification.create
data:
title: "Cheap electricity"
message: >
Price is {{ state_attr('binary_sensor.essent_cheap_electricity', 'current_price') }}
(threshold {{ state_attr('binary_sensor.essent_cheap_electricity', 'threshold') }})A ready-to-use Lovelace dashboard is provided for the integration. It shows the current and next price, today's average, a cheap-electricity indicator, a 24-hour price curve, the cheapest 3‑hour block, tomorrow's preview, and gas prices. Cards are generated only for the sensors your provider/tariff actually creates, and use each configured entry's real entity IDs (respecting any renames you've made).
The dashboard installs itself into Home Assistant — no copy-pasting or prefix-replacing:
- Options flow: open Settings > Devices & Services > Dynamic Energy Prices > Configure, tick Install or update the Energy Prices dashboard, and submit. The dashboard appears as Energy Prices in the sidebar.
- Service: call
dynamic_energy_prices.install_dashboardat any time to re-create or update it (for example from a script or automation).
To remove it again, tick Uninstall the Energy Prices dashboard in the same
options flow (or call dynamic_energy_prices.uninstall_dashboard). This
removes the dashboard, its sidebar panel, and its config.
Once installed, the dashboard is automatically rebuilt on every Home Assistant start (after an upgrade or restart) so new cards — like the built-in price-curve fallback — are picked up without re-running the install. It only refreshes if you already installed it; it never creates one for you.
If you run multiple config entries (e.g. EnergyZero electricity + Frank
Energie gas), installing merges every entry into one dashboard with a tab per
provider (only tabs for providers whose sensors are enabled are shown). The
price-curve card is optional and off by default: enable Include 'Today's
price curve' card in the options. It uses the
apexcharts-card custom card
(HACS > Frontend) when that's installed; otherwise it automatically falls
back to Home Assistant's built-in chart card, so the curve always works.
If your Lovelace is in YAML mode the automatic install isn't possible; the integration logs a warning and you can fall back to the static example below.
examples/dashboard.yaml is a static reference
copy of the same dashboard. To use it: copy the file's contents into a new
dashboard (Settings > Dashboards > Add Dashboard > New dashboard from
YAML, or edit an existing dashboard in YAML mode), then replace the essent
entity prefix with your own provider's prefix.
| Service | Target | Description |
|---|---|---|
force_update |
sensor, binary_sensor |
Force refresh price data from the provider |
install_dashboard |
— | Install or update the Energy Prices dashboard for all configured entries |
The integration polls your provider's API every 60 minutes with a random start-minute offset (0–59) to spread load evenly across users. Each request has a 15-second timeout. If a request fails, the coordinator keeps the last successful data and retries on the next cycle.
After 3 consecutive failures a repair issue is created in Settings > Repairs with the error details. It is cleared automatically on the next successful fetch.
- Real-time monitoring of current, next, average, lowest, and highest electricity and gas prices
- Breakdown sensors showing the market price, supplier markup, and energy tax components of the current electricity price
- Tomorrow price preview (for EnergyZero, Eneco, and Frank Energie)
- Cheapest 3‑hour block sensor with
device_class: timestamp— state is the start time of the cheapest remaining block of the day - Binary sensor that indicates when the current price is below a configurable threshold (defaults to the day's average price)
- Diagnostics sensors showing the last update time and next scheduled update
- Force‑update service to trigger an immediate refresh
hourly_pricesattribute oncurrent_electricity_pricespanning all available days (yesterday + today + tomorrow for Essent), for scheduling across midnight- Multi‑provider support: run multiple config entries simultaneously (e.g., EnergyZero for electricity + Frank Energie for gas)
- Essent does not offer a date-based API, so tomorrow-price sensors are not created for Essent config entries.
- Vattenfall TijdPrijs is a fixed time-of-use tariff, not a real-time dynamic price — it cannot be implemented as a dynamic price provider.
- The cheapest 3‑hour block only considers the remaining hours of the current day. It will not find a block if fewer than 3 hours are left until midnight.
- The binary sensor compares the current hour's price against the day's average. It uses the average of all 24 hours, so early in the day the average is less representative.
- Providers may return prices with different timezones. The integration uses
the provider's native timezone where possible (e.g.,
Europe/Amsterdamfor Essent).
| Symptom | Likely cause | Fix |
|---|---|---|
| No entities created after setup | API key issue or network | Check HA logs for UpdateFailed errors |
| Essent shows "401 Unauthorized" | Missing x-request-origin header (fixed in v0.8.1) |
Update to the latest version |
'HomeAssistant' object has no attribute 'issues' |
HA version predates the issues API (pre-2023.6) | Update HA or ignore — no functional impact (fixed in v0.15.1) |
| Entity "no longer being provided" | Setup failed during refresh | Restart HA after updating to latest version |
| Tomorrow sensors missing | Provider does not support date-based queries (Essent) | Expected — not available for Essent |
| Gas sensors missing | Provider returned no gas data, or only electricity is available | Expected — check provider capabilities |
| Prices seem wrong / outdated | Data is polled every 60 minutes | Use the force_update service to trigger an immediate refresh |
See providers/base.py
for the PriceProvider ABC and dataclasses.
- Create
custom_components/dynamic_energy_prices/providers/<name>.py. - Subclass
PriceProvider, setprovider_idanddisplay_name. - Implement
async_fetch_prices()returningProviderPrices. - Import the module in
providers/__init__.pyfor auto-registration. No other files need changes.
# Install test dependencies (Windows: pip install tzdata as well)
pip install -r requirements_test.txt
# Run all tests
pytest --asyncio-mode=auto -v
# Run tests for a specific module
pytest tests/test_sensor.py tests/test_binary_sensor.py -v
pytest tests/test_essent_provider.py -v
pytest tests/test_energyzero_provider.py -v
pytest tests/test_frank_energie_provider.py -v
# Smoke-test against live APIs
python scripts/smoke_test_essent.py
python scripts/smoke_test_energyzero.py
python scripts/smoke_test_frank_energie.py- Essent provider
- EnergyZero provider
- Frank Energie provider (NL + BE)
- Pluggable provider architecture
- Eneco NL (EnergyZero alias provider)
- Config flow with provider-specific options (Belgium toggle)
- Belgium (BE) support toggle in Frank Energie config
- Force-update service
- Tomorrow's prices sensors
- Standardised breakdown sensors (market price, supplier markup, energy tax)
- Cheap electricity binary sensor (current < average)
- Cheapest 3-hour block sensor (sliding window, TIMESTAMP device class)
- Options flow with custom threshold
- Diagnostics sensors (last updated, next update)
- Repair/issue flow for API errors
- Silver quality scale
Structural skeleton adapted from jaapp/ha-essent-dynamic (MIT). See NOTICE.
MIT