-
Notifications
You must be signed in to change notification settings - Fork 1
Zonneplan
The zonneplan_prices helper fetches hourly dynamic electricity prices from the Zonneplan API and publishes them in mimirheim's expected JSON format. It is a drop-in replacement for the Nordpool helper for users whose energy supplier is Zonneplan.
Zonneplan is a Dutch energy supplier that publishes dynamic (hourly) all-in consumer prices. No API key is required — authentication uses an email one-time-password (OTP) flow that the daemon handles automatically.
Full auto-generated field reference: Config-Zonneplan
You can configure and enable this helper from the browser UI instead of editing YAML directly — see Helpers/Config-Editor for setup instructions.
- Subscribes to
trigger_topic. - On trigger: checks whether a valid access token is available on disk.
- If no token exists, or the token has expired and cannot be refreshed, the daemon sends a login email to the configured address and begins polling for activation. The user must click the link in the email once.
- Fetches the
price_per_hourlist from the Zonneplan API for the current connection. - Applies the
import_formulaandexport_formulato compute all-in prices per step. - Publishes a JSON array of timestamped steps to
output_topic(retained). - Optionally publishes to
mimir_trigger_topicto start a mimirheim solve immediately.
The payload format is:
[
{
"ts": "2026-05-28T10:00:00+00:00",
"import_eur_per_kwh": 0.154619,
"export_eur_per_kwh": 0.0,
"confidence": 1.0
}
]All timestamps are UTC. The confidence value is always 1.0 (Zonneplan publishes confirmed hourly prices, not forecasts).
Zonneplan uses an email OTP flow. The daemon handles the entire flow automatically:
- First run with no token: the daemon logs a WARNING and sends a login email to the configured address. Check your inbox and click the activation link.
-
Polling: while waiting for the click, each trigger cycle logs a WARNING (
Still waiting for Zonneplan activation — click the link...). No manual restart is needed. -
After activation: the daemon saves the token to
token_fileand immediately fetches prices. The activation link is only needed once. - Subsequent runs: the access token is refreshed automatically. A new login email is only sent again if the refresh token itself expires (typically after weeks or months).
The pending-auth state is persisted to a _pending.json file alongside the token file. If the container restarts mid-authentication, the daemon resumes polling the same activation UUID rather than sending a new email.
The token_file path must be on a persistent volume. If it lives inside the container filesystem, it is lost on every container restart and the email activation must be repeated each time.
When running as a Home Assistant add-on, /config is already a persistent volume — set token_file: /config/zonneplan_token.json.
For plain Docker, mount a host directory and set token_file to a path within it:
# docker-compose.yml (excerpt)
volumes:
- ./data:/data# config.yaml
zonneplan:
token_file: /data/zonneplan_token.jsonZonneplan publishes the next day's prices at some point in the afternoon. Running the trigger at 15:00 and again at 17:00 UTC covers the typical publication window reliably:
schedules:
- "0 15 * * *": mimir/input/tools/prices/trigger
- "0 17 * * *": mimir/input/tools/prices/triggerThe Zonneplan API returns two price values per hour:
-
price— the all-in consumer import price including VAT (EUR/kWh) -
price_excl_tax— the import price excluding VAT (EUR/kWh)
The import_formula and export_formula are Python expressions evaluated at runtime for each hourly step. Three variables are available:
| Variable | Type | Description |
|---|---|---|
price |
float | All-in import price incl. VAT, EUR/kWh |
price_excl_tax |
float | Import price excl. VAT, EUR/kWh |
ts |
datetime | Step start time (UTC-aware) |
The formula is evaluated directly as Python. The config file is treated as operator-controlled code (like a shell script); do not load it from untrusted sources.
Pass through the all-in price unchanged (default):
zonneplan:
import_formula: "price"
export_formula: "price_excl_tax"Add a fixed per-kWh supplier markup on top of the excl-tax price:
zonneplan:
import_formula: "price_excl_tax * 1.21 + 0.05"Time-of-use feed-in credit (lower overnight):
zonneplan:
export_formula: "price_excl_tax * (0.8 if 7 <= ts.hour <= 22 else 0.5)"mqtt:
host: localhost
port: 1883
client_id: mimir-zonneplan-prices
zonneplan:
email: [email protected]
token_file: /config/zonneplan_token.json
import_formula: "price"
export_formula: "price_excl_tax"
mimir_topic_prefix: mimir
signal_mimir: true
# mimir_trigger_topic defaults to: mimir/input/trigger
ha_discovery:
enabled: true
device_name: "MIMIRHEIM Zonneplan"
stats_topic: mimirheim-helpers/stats/zonneplan-pricesWith mimir_topic_prefix: mimir:
| Config field | Derived topic |
|---|---|
output_topic |
mimir/input/prices |
mimir_trigger_topic |
mimir/input/trigger |
Set ha_discovery.forecast_sensor: true to publish a sensor entity that shows the current next-step import price and exposes the full forecast array as JSON attributes.
-
State:
import_eur_per_kwhof the first (nearest-future) step, rounded to 4 decimal places -
Unit:
EUR/kWh— no HAdevice_class(non-standard unit) -
Attributes:
forecastkey containing the full array. Each step:import(EUR/kWh, 4 dp),export(EUR/kWh, 4 dp),c(confidence, 2 dp),ts. -
Enabled by default:
true
ha_discovery:
enabled: true
forecast_sensor: trueExample apexcharts-card transform to plot import price over time:
- entity: sensor.zonneplan_prices_forecast
attribute: forecast
transform: "return x.map(s => [new Date(s.ts).getTime(), s.import])"Getting started
Helpers
- Common
- Nordpool
- Zonneplan
- PV Fetcher
- PV ML Learner
- Baseload (Static)
- Baseload (HA)
- Baseload (HA DB)
- Reporter
- Scheduler
- Config Editor
Developer
Reference