Skip to content

Legolas-2025/EPrices

Repository files navigation

EPrices Header

EPrices

An ESPHome-based ESP32 firmware that fetches day-ahead electricity spot prices from the public Energy-Charts API and exposes them as Home Assistant sensors — with no cloud subscription, no API token, and no external dependencies beyond your WiFi network.

All core logic runs entirely on the ESP32 — midnight bridge, boot recovery, retry scheduling, and NVS persistence are all on-device. No external Home Assistant automations are required for any core functionality.


Features

  • Fetches 15-minute resolution spot prices for today and tomorrow
  • Automatically applies your provider fee and VAT rate to raw €/MWh prices — with separate fee configuration for negative spot prices
  • Exposes 96 per-day price points plus 24-hour averages as JSON text sensors for use in HA automations and dashboards
  • NVS persistence — prices survive device reboots without re-fetching
  • Midnight bridge — tomorrow's data automatically becomes today at 00:00, fully on-device
  • Auto-retry logic — up to 8 HTTP fetch attempts for both today and tomorrow, with a 120-second stuck-fetch watchdog that unblocks retries after TCP-level stalls
  • Task watchdog stability — watchdog timeout increased to 40s, idle task checking disabled; periodic yield() calls and optimised JSON building prevent spontaneous reboots during heavy parsing when full price data arrives (~13:55)
  • DST-safe — uses UNIX timestamps and binary search throughout, no hour-slot arithmetic
  • Staleness detectionToday Current Price Status shows Stale if stored date mismatches today
  • Tomorrow live sensors evaluate at now + 86400s — reflecting tomorrow at the same local time
  • Supports any Energy-Charts bidding zone (SI, DE-LU, AT, FR, HR, HU and more)
  • Full diagnostic sensor suite — NVS status, fetch attempts, API fetch times, data loaded times, WiFi signal, human-readable uptime
  • All fee and VAT settings configurable via secrets.yamlno code changes needed

Hardware

  • ESP32 development board (tested on esp32dev)
  • ESPHome with esp-idf framework (required for NVS flash support)

Note on ESP32 variants: EPrices is compiled and tested on the original dual-core ESP32 (esp32dev). It also runs on ESP32-S3 with a one-line board change. Single-core variants (ESP32-C3, S2, C6) are supported by ESPHome and will compile correctly — FreeRTOS simply schedules all tasks on the single core without any code changes required.


Files

File Purpose
eprices.yaml Main ESPHome configuration
eprices_nvs.h NVS helper — save/load price arrays to ESP32 flash
secrets.yaml Your local secrets (not committed to git)
CHANGELOG.md Complete sensor and entity ID reference
VERSION.md Version history and release notes
ENTSO-E-PRICES-MIGRATION.md Optional — migration guide from the predecessor project

Installation

1) Clone or download this repository

2) Create your secrets.yaml

Place secrets.yaml in the same directory as eprices.yaml. It must contain the following keys:

wifi_ssid: "your_wifi_ssid"
wifi_password: "your_wifi_password"
eprices_fallback_ap_ssid: "EPrices-Fallback"
eprices_fallback_ap_password: "your_fallback_ap_password"
eprices_api_encryption_key: "your_base64_api_encryption_key_here"
eprices_timezone: "Europe/Ljubljana"
eprices_country_bzn: "SI"
eprices_prov_fee: "0.12"
eprices_vat_rate: "0.22"
eprices_neg_prov_fee: "0.30"

Key descriptions:

Key Description
eprices_timezone Your local timezone — must match a valid IANA tz name
eprices_country_bzn Energy-Charts bidding zone code — see supported zones
eprices_prov_fee Provider fee for positive spot prices — decimal multiplier, e.g. "0.12" = 12%
eprices_vat_rate VAT rate — decimal multiplier, e.g. "0.22" = 22%
eprices_neg_prov_fee Provider fee kept on negative spot prices — decimal multiplier, e.g. "0.30" means provider keeps 30%, pays you 70%; use "0.00" if provider passes the full negative price to you
eprices_api_encryption_key 32-byte base64 key for HA API encryption — generate with openssl rand -base64 32

3) Flash to your ESP32

esphome run eprices.yaml

Or use the ESPHome Dashboard or the HA ESPHome add-on.

4) Add to Home Assistant

The device will appear automatically in HA via the ESPHome integration. Accept the device and enter your API encryption key when prompted.


Supported bidding zones

Any zone supported by the Energy-Charts API /price endpoint. Common examples:

Code Zone
SI Slovenia
DE-LU Germany / Luxembourg
AT Austria
FR France
IT-North Italy North
HR Croatia
HU Hungary

For the full list see the Energy-Charts API documentation.


How it works

Price fetch schedule

Event Action
Boot Load today + tomorrow from NVS flash; HTTP fetch if NVS miss or stale
00:00 midnight Promote tomorrow → today; clear tomorrow; schedule tomorrow fetch if in window
00:05, 00:15, 00:30, then hourly :30 Auto-retry today fetch if previous attempt failed (max 8)
13:25 First auto-fetch attempt for tomorrow
13:55, 14:55 … 19:55 Retry tomorrow fetch if previous attempt failed (max 8 total)
Manual button press Force immediate fetch for today or tomorrow

Stuck-fetch watchdog

If an HTTP request stalls at the TCP level (connection accepted but data delivery suspended), the 25 s application timeout may not fire. In that case the is_updating_* flag can stay true for several minutes, blocking all subsequent retries and manual button presses.

The worker loop checks every 10 seconds: if is_updating_today or is_updating_tomorrow has been true for more than 120 seconds, the flag is force-cleared and the status message is set to "Fetch timeout – will retry". The next scheduled trigger or manual press then proceeds normally.

Price calculation

Raw prices from the API are in €/MWh. EPrices converts them to €/kWh and applies your provider fee and VAT. Positive and negative spot prices use separate fee multipliers, configurable independently in secrets.yaml:

Market price Formula
Positive (raw ≥ 0) (raw / 1000) × (1 + prov_fee) × (1 + vat_rate)
Negative (raw < 0) (raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate)

The switch happens on the raw API price before any multiplier is applied. VAT is applied to both cases, consistent with net billing contracts where VAT is calculated on the monthly net sum (mathematically equivalent due to VAT being a linear multiplier).

Example with prov_fee = 0.12, neg_prov_fee = 0.30, vat_rate = 0.22:

  • Spot price +100 €/MWh(100/1000) × 1.12 × 1.22 = 0.1366 €/kWh (you pay)
  • Spot price −50 €/MWh(−50/1000) × 0.70 × 1.22 = −0.0427 €/kWh (you receive)

Negative prices

On days with high solar or wind generation, spot prices can go negative. EPrices handles negative prices correctly throughout. In the 15-min JSON sensors, negative values are formatted with 3 decimal places (e.g. -0.057) instead of 4, to stay within the 255-character Home Assistant text sensor state limit. Precision loss is at most 0.0001 €/kWh (0.01 cent).

NVS persistence

Prices are stored in ESP32 NVS (non-volatile storage) under the eprices namespace using dynamic timestamp + price arrays. On reboot, stored prices are validated against today's and tomorrow's date before use. Stale data is discarded and a fresh HTTP fetch is triggered automatically.

DST safety

All price indexing uses UNIX timestamps and binary search. There is no hour-slot arithmetic anywhere in the codebase, making the firmware fully safe on DST transition days (23-hour and 25-hour days).


Sensors

Today — numeric

Sensor Entity ID Description
Today Current Price sensor.eprices_today_current_price Current 15-min slot price €/kWh
Today Next Price sensor.eprices_today_next_price Next 15-min slot price €/kWh
Today Average Price sensor.eprices_today_average_price Average of all hourly prices €/kWh
Today Highest Price sensor.eprices_today_highest_price Highest 15-min price €/kWh
Today Lowest Price sensor.eprices_today_lowest_price Lowest 15-min price €/kWh
Today Current Hourly Price sensor.eprices_today_current_hourly_price Current hour average €/kWh
Today Next Hourly Price sensor.eprices_today_next_hourly_price Next hour average €/kWh
Today Highest Hourly Price sensor.eprices_today_highest_hourly_price Highest hourly average €/kWh
Today Lowest Hourly Price sensor.eprices_today_lowest_hourly_price Lowest hourly average €/kWh
Today Current Max Hourly Price Percentage sensor.eprices_today_current_max_hourly_price_percentage Current hour as % of daily max

Today — text

Sensor Entity ID Description
Today JSON Hourly Prices EUR⁄kWh sensor.eprices_today_json_hourly_prices_eur_kwh JSON array of 24 hourly averages; "" when no data
Today JSON 15-Min Prices EUR⁄kWh (P1 00:00-07:45) sensor.eprices_today_json_15_min_prices_eur_kwh_p1_00_00_07_45 JSON array of 32 prices; "" when no data
Today JSON 15-Min Prices EUR⁄kWh (P2 08:00-15:45) sensor.eprices_today_json_15_min_prices_eur_kwh_p2_08_00_15_45 JSON array of 32 prices; "" when no data
Today JSON 15-Min Prices EUR⁄kWh (P3 16:00-23:45) sensor.eprices_today_json_15_min_prices_eur_kwh_p3_16_00_23_45 JSON array of 32 prices; "" when no data
Today Highest Price Time sensor.eprices_today_highest_price_time Time of highest 15-min price (HH:MM)
Today Lowest Price Time sensor.eprices_today_lowest_price_time Time of lowest 15-min price (HH:MM)
Today Highest Hourly Price Time sensor.eprices_today_highest_hourly_price_time Time of highest hourly average (HH:00)
Today Lowest Hourly Price Time sensor.eprices_today_lowest_hourly_price_time Time of lowest hourly average (HH:00)
Today Current Price Status sensor.eprices_today_current_price_status Valid / Missing / Stale
Today Data Loaded Time sensor.eprices_today_data_loaded_time Last time today data entered memory (NVS or HTTP)

Tomorrow — numeric

Sensor Entity ID Description
Tomorrow Current Price sensor.eprices_tomorrow_current_price Tomorrow at same local time as now
Tomorrow Next Price sensor.eprices_tomorrow_next_price Tomorrow next 15-min slot
Tomorrow Average Price sensor.eprices_tomorrow_average_price Average of all hourly prices €/kWh
Tomorrow Highest Price sensor.eprices_tomorrow_highest_price Highest 15-min price €/kWh
Tomorrow Lowest Price sensor.eprices_tomorrow_lowest_price Lowest 15-min price €/kWh
Tomorrow Current Hourly Price sensor.eprices_tomorrow_current_hourly_price Tomorrow same hour average €/kWh
Tomorrow Next Hourly Price sensor.eprices_tomorrow_next_hourly_price Tomorrow next hour average €/kWh
Tomorrow Highest Hourly Price sensor.eprices_tomorrow_highest_hourly_price Highest hourly average €/kWh
Tomorrow Lowest Hourly Price sensor.eprices_tomorrow_lowest_hourly_price Lowest hourly average €/kWh
Tomorrow Current Max Hourly Price Percentage sensor.eprices_tomorrow_current_max_hourly_price_percentage Same-time hour as % of tomorrow max

Tomorrow — text

Sensor Entity ID Description
Tomorrow JSON Hourly Prices EUR⁄kWh sensor.eprices_tomorrow_json_hourly_prices_eur_kwh JSON array of 24 hourly averages; "" when no data
Tomorrow JSON 15-Min Prices EUR⁄kWh (P1 00:00-07:45) sensor.eprices_tomorrow_json_15_min_prices_eur_kwh_p1_00_00_07_45 JSON array of 32 prices; "" when no data
Tomorrow JSON 15-Min Prices EUR⁄kWh (P2 08:00-15:45) sensor.eprices_tomorrow_json_15_min_prices_eur_kwh_p2_08_00_15_45 JSON array of 32 prices; "" when no data
Tomorrow JSON 15-Min Prices EUR⁄kWh (P3 16:00-23:45) sensor.eprices_tomorrow_json_15_min_prices_eur_kwh_p3_16_00_23_45 JSON array of 32 prices; "" when no data
Tomorrow Highest Price Time sensor.eprices_tomorrow_highest_price_time Time of highest 15-min price (HH:MM)
Tomorrow Lowest Price Time sensor.eprices_tomorrow_lowest_price_time Time of lowest 15-min price (HH:MM)
Tomorrow Highest Hourly Price Time sensor.eprices_tomorrow_highest_hourly_price_time Time of highest hourly average (HH:00)
Tomorrow Lowest Hourly Price Time sensor.eprices_tomorrow_lowest_hourly_price_time Time of lowest hourly average (HH:00)
Tomorrow Current Price Status sensor.eprices_tomorrow_current_price_status Valid / Missing / Waiting...
Tomorrow Data Loaded Time sensor.eprices_tomorrow_data_loaded_time Last time tomorrow data entered memory; Outside fetch window before 13:20

Diagnostic

Sensor Entity ID Description
Last Reboot sensor.eprices_last_reboot Timestamp of last device boot
Uptime sensor.eprices_uptime Human-readable: 45 s / 5 min / 3 h 22 min / 12 d 4 h / 4 months 12 d
WiFi Signal sensor.eprices_wifi_signal RSSI in dBm, updated every 60s
Last Update Source sensor.eprices_last_update_source NVS_boot / HTTP_today / midnight_bridge / NVS_api etc.
Today Data Date sensor.eprices_today_data_date Date of currently stored today data
Tomorrow Data Date sensor.eprices_tomorrow_data_date Date of currently stored tomorrow data
Today Entry Count sensor.eprices_today_entry_count Number of price points stored for today
Tomorrow Entry Count sensor.eprices_tomorrow_entry_count Number of price points stored for tomorrow
Today NVS Status sensor.eprices_today_nvs_status NVS load/store result for today
Tomorrow NVS Status sensor.eprices_tomorrow_nvs_status NVS load/store result for tomorrow
Today Last API Fetch Time sensor.eprices_today_last_api_fetch_time Timestamp of last successful HTTP fetch for today; Never if NVS only
Tomorrow Last API Fetch Time sensor.eprices_tomorrow_last_api_fetch_time Timestamp of last successful HTTP fetch for tomorrow; Never if NVS only
Today API Fetch Attempts sensor.eprices_today_api_fetch_attempts HTTP fetch attempt count for today; resets at midnight
Tomorrow API Fetch Attempts sensor.eprices_tomorrow_api_fetch_attempts HTTP fetch attempt count for tomorrow; resets at 13:25
Today Price Update Status sensor.eprices_today_price_update_status SUCCESS / FAILED/WAITING
Tomorrow Price Update Status sensor.eprices_tomorrow_price_update_status SUCCESS / FAILED/WAITING
Today Price Update Status Message sensor.eprices_today_price_update_status_message Detailed status string
Tomorrow Price Update Status Message sensor.eprices_tomorrow_price_update_status_message Detailed status string; "Fetch timeout – will retry" after watchdog trigger

Buttons

Button Entity ID Description
Force Today's Update button.eprices_force_today_s_update Trigger immediate today HTTP fetch
Force Tomorrow's Update button.eprices_force_tomorrow_s_update Trigger immediate tomorrow fetch (window: 13:20–23:50)
Reboot Device button.eprices_reboot_device Restart the ESP32

Migrating from entso-e-prices

If you are migrating from the predecessor project entso-e-prices v4.3.1, see ENTSO-E-PRICES-MIGRATION.md for the complete rationale, all sensor rename mappings, and a migration checklist.


Data attribution

Electricity price data provided by Energy-Charts (Fraunhofer ISE) via the Energy-Charts API, licensed under CC BY 4.0.


License

This project (ESPHome YAML and C++ helper) is released under the MIT License.

About

ESP32 + ESPHome firmware that fetches day-ahead electricity spot prices from the Energy-Charts API and exposes them as Home Assistant sensors. No token, no subscription. Supports 15-min and hourly resolution, NVS persistence, auto-retry, DST-safe, and any Energy-Charts bidding zone.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages