6 Commits
Author SHA1 Message Date
Amir 7faaaceed0 Update README with fee configuration details (v1.1)
Clarify configuration for provider fees and VAT rates:
negative price provider fee support added in EPrices v1.1
2026-04-06 03:27:38 +02:00
Amir 6f3ba88394 Add negative price provider fee support in VERSION.md
Introduced support for negative provider fees with a new secret key and updated price calculation methods for both positive and negative prices.
2026-04-06 03:19:58 +02:00
Amir 134cb7bbb1 Add negative price provider fee support in v1.1
Added support for negative price provider fees, configurable via secrets.yaml. Updated price calculation formulas and modified eprices.yaml for new global and substitutions.
2026-04-06 03:18:17 +02:00
Amir ee48ef13f4 Update secrets.yaml with negative price fee multiplier
Added provider fee multiplier for negative spot prices.
2026-04-06 03:15:59 +02:00
Amir a09de2a71b Upgrade EPrices to version 1.1 with new features
Updated version to 1.1 with new features including separate provider fee for negative prices and new substitutions. Adjusted calculations for electricity prices based on the new fee structure.
2026-04-06 03:14:23 +02:00
Amir fa507ff8ef Add files via upload 2026-04-05 21:48:35 +02:00
5 changed files with 227 additions and 25 deletions
+47 -1
View File
@@ -1,4 +1,50 @@
# EPrices v1.0 Changelog # EPrices Changelog
## v1.1 — 2026-04-06
### Negative price provider fee support
Added a separate provider fee multiplier for negative spot prices, configurable
via `secrets.yaml`. This correctly models contracts where the provider fee
structure differs between positive and negative market prices.
**New secret key:**
```yaml
eprices_neg_prov_fee: "0.30"
```
**New global:** `neg_prov_fee` (type: `double`)
**Price calculation is now:**
| Market price | Formula |
|---|---|
| Positive (`raw_mwh >= 0`) | `(raw / 1000) × (1 + prov_fee) × (1 + vat_rate)` |
| Negative (`raw_mwh < 0`) | `(raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate)` |
**Key values for `eprices_neg_prov_fee`:**
| Value | Meaning |
|---|---|
| `"0.30"` | Provider keeps 30%, pays you 70% of negative price |
| `"0.00"` | Provider passes full negative price to you (no fee) |
**Switch point:** raw API price (`raw_mwh`) before any multiplier is applied.
**VAT:** applied symmetrically to both positive and negative prices, consistent
with net billing models where VAT is calculated on the monthly net sum
(mathematically equivalent due to VAT being a linear multiplier).
**Changed files:** `eprices.yaml`, `secrets.yaml`
**Changed locations in `eprices.yaml`:**
- `substitutions:` — added `neg_prov_fee_value`
- `globals:` — added `neg_prov_fee`
- `parse_energy_charts_today_script``MULT` split into `MULT_POS` / `MULT_NEG`
- `parse_energy_charts_tomorrow_script``MULT` split into `MULT_POS` / `MULT_NEG`
---
## v1.0 — 2026-04-05 ## v1.0 — 2026-04-05
+23 -9
View File
@@ -16,7 +16,8 @@ Assistant automations are required for any core functionality.
## Features ## Features
- Fetches **15-minute resolution** spot prices for **today** and **tomorrow** - Fetches **15-minute resolution** spot prices for **today** and **tomorrow**
- Automatically applies your **provider fee** and **VAT rate** to raw €/MWh prices - 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 - Exposes **96 per-day price points** plus **24-hour averages** as JSON text sensors
for use in HA automations and dashboards for use in HA automations and dashboards
- **NVS persistence** — prices survive device reboots without re-fetching - **NVS persistence** — prices survive device reboots without re-fetching
@@ -28,7 +29,7 @@ Assistant automations are required for any core functionality.
- Supports **any Energy-Charts bidding zone** (SI, DE-LU, AT, FR, HR, HU and more) - 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, - Full **diagnostic sensor suite** — NVS status, fetch attempts, API fetch times,
data loaded times, WiFi signal, human-readable uptime data loaded times, WiFi signal, human-readable uptime
- Provider fee and VAT rate configurable via `secrets.yaml`**no code changes needed** - All fee and VAT settings configurable via `secrets.yaml`**no code changes needed**
--- ---
@@ -46,7 +47,7 @@ Assistant automations are required for any core functionality.
| `eprices.yaml` | Main ESPHome configuration | | `eprices.yaml` | Main ESPHome configuration |
| `eprices_nvs.h` | NVS helper — save/load price arrays to ESP32 flash | | `eprices_nvs.h` | NVS helper — save/load price arrays to ESP32 flash |
| `secrets.yaml` | Your local secrets (not committed to git) | | `secrets.yaml` | Your local secrets (not committed to git) |
| `CHANGELOG.md` | Complete sensor and entity ID reference for v1.0 | | `CHANGELOG.md` | Complete sensor and entity ID reference |
| `VERSION.md` | Version history and release notes | | `VERSION.md` | Version history and release notes |
| `ENTSO-E-PRICES-MIGRATION.md` | Optional — migration guide from the predecessor project | | `ENTSO-E-PRICES-MIGRATION.md` | Optional — migration guide from the predecessor project |
@@ -71,6 +72,7 @@ eprices_timezone: "Europe/Ljubljana"
eprices_country_bzn: "SI" eprices_country_bzn: "SI"
eprices_prov_fee: "0.12" eprices_prov_fee: "0.12"
eprices_vat_rate: "0.22" eprices_vat_rate: "0.22"
eprices_neg_prov_fee: "0.30"
``` ```
**Key descriptions:** **Key descriptions:**
@@ -79,8 +81,9 @@ eprices_vat_rate: "0.22"
|---|---| |---|---|
| `eprices_timezone` | Your local timezone — must match a valid [IANA tz name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | | `eprices_timezone` | Your local timezone — must match a valid [IANA tz name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) |
| `eprices_country_bzn` | Energy-Charts bidding zone code — see [supported zones](#supported-bidding-zones) | | `eprices_country_bzn` | Energy-Charts bidding zone code — see [supported zones](#supported-bidding-zones) |
| `eprices_prov_fee` | Provider fee as a decimal multiplier, e.g. `"0.12"` = 12% | | `eprices_prov_fee` | Provider fee for **positive** spot prices — decimal multiplier, e.g. `"0.12"` = 12% |
| `eprices_vat_rate` | VAT rate as a decimal multiplier, e.g. `"0.22"` = 22% | | `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` | | `eprices_api_encryption_key` | 32-byte base64 key for HA API encryption — generate with `openssl rand -base64 32` |
### 3) Flash to your ESP32 ### 3) Flash to your ESP32
@@ -133,11 +136,22 @@ For the full list see the [Energy-Charts API documentation](https://api.energy-c
### Price calculation ### Price calculation
Raw prices from the API are in **€/MWh**. EPrices converts them to **€/kWh** Raw prices from the API are in **€/MWh**. EPrices converts them to **€/kWh**
and applies your provider fee and VAT: and applies your provider fee and VAT. Positive and negative spot prices use
separate fee multipliers, configurable independently in `secrets.yaml`:
``` | Market price | Formula |
price_eur_kwh = (raw_eur_mwh / 1000) × (1 + prov_fee) × (1 + vat_rate) |---|---|
``` | 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 ### Negative prices
+20
View File
@@ -1,5 +1,25 @@
# EPrices Version History # EPrices Version History
## v1.1 — 2026-04-06
### Negative price provider fee
Added separate provider fee support for negative spot prices via a new
`eprices_neg_prov_fee` secret key. Positive and negative market prices
now use independent fee multipliers, correctly modelling contracts where
the provider's fee structure differs between the two cases.
Price calculation:
- Positive: `(raw / 1000) × (1 + prov_fee) × (1 + vat_rate)`
- Negative: `(raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate)`
VAT is applied to both, consistent with net billing where VAT is calculated
on the monthly net sum (linear equivalence applies).
See `CHANGELOG.md` for full implementation details.
---
## v1.0 — 2026-04-05 ## v1.0 — 2026-04-05
First public release. First public release.
+24 -9
View File
@@ -1,13 +1,22 @@
# ============================================================================= # =============================================================================
# ESPHome: EPrices v1.0 # ESPHome: EPrices v1.1
#
# ESP32 firmware that fetches day-ahead electricity spot prices from the
# public Energy-Charts API (https://api.energy-charts.info) and exposes
# them as Home Assistant sensors. No API token required.
# #
# Electricity price data provided by Energy-Charts (https://energy-charts.info) # Electricity price data provided by Energy-Charts (https://energy-charts.info)
# under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). # under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/).
# #
# v1.1 changes:
# - Separate provider fee for negative spot prices (eprices_neg_prov_fee):
# positive prices: (raw / 1000) × (1 + prov_fee) × (1 + vat_rate)
# negative prices: (raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate)
# switch happens on raw API price before any multiplier is applied
# VAT applied to both cases (consistent with net billing + end-of-month VAT)
# 0.30 = provider keeps 30%, pays you 70% of negative price
# 0.00 = provider passes full negative price to you (no fee)
# - New substitution: neg_prov_fee_value (!secret eprices_neg_prov_fee)
# - New global: neg_prov_fee (double)
# - MULT split into MULT_POS / MULT_NEG in parse_energy_charts_today_script
# and parse_energy_charts_tomorrow_script
#
# v1.0 release notes: # v1.0 release notes:
# - Fetches 15-min and hourly spot prices for today and tomorrow # - Fetches 15-min and hourly spot prices for today and tomorrow
# - Prices converted from raw €/MWh to €/kWh with provider fee and VAT applied # - Prices converted from raw €/MWh to €/kWh with provider fee and VAT applied
@@ -43,6 +52,7 @@ substitutions:
country_bzn_value: !secret eprices_country_bzn country_bzn_value: !secret eprices_country_bzn
prov_fee_value: !secret eprices_prov_fee prov_fee_value: !secret eprices_prov_fee
vat_rate_value: !secret eprices_vat_rate vat_rate_value: !secret eprices_vat_rate
neg_prov_fee_value: !secret eprices_neg_prov_fee
esphome: esphome:
name: eprices name: eprices
@@ -497,6 +507,9 @@ globals:
- id: vat_rate - id: vat_rate
type: double type: double
initial_value: '${vat_rate_value}' initial_value: '${vat_rate_value}'
- id: neg_prov_fee
type: double
initial_value: '${neg_prov_fee_value}'
# --- Pre-computed fetch URLs (url: !lambda not supported in scripts) --- # --- Pre-computed fetch URLs (url: !lambda not supported in scripts) ---
- id: fetch_url_today - id: fetch_url_today
@@ -1261,7 +1274,8 @@ script:
raw_body: string raw_body: string
then: then:
- lambda: |- - lambda: |-
const double MULT = (1.0 + id(prov_fee)) * (1.0 + id(vat_rate)); const double MULT_POS = (1.0 + id(prov_fee)) * (1.0 + id(vat_rate));
const double MULT_NEG = (1.0 - id(neg_prov_fee)) * (1.0 + id(vat_rate));
id(price_values_today).clear(); id(price_values_today).clear();
id(price_timestamps_today).clear(); id(price_timestamps_today).clear();
@@ -1350,7 +1364,7 @@ script:
if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) continue; if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) continue;
if (unix_ts <= 0) continue; if (unix_ts <= 0) continue;
float eur_kwh = (float)((double)raw_mwh * MULT / 1000.0); float eur_kwh = (float)((double)raw_mwh * (raw_mwh >= 0 ? MULT_POS : MULT_NEG) / 1000.0);
id(price_timestamps_today).push_back(unix_ts); id(price_timestamps_today).push_back(unix_ts);
id(price_values_today).push_back(eur_kwh); id(price_values_today).push_back(eur_kwh);
} }
@@ -1369,7 +1383,8 @@ script:
raw_body: string raw_body: string
then: then:
- lambda: |- - lambda: |-
const double MULT = (1.0 + id(prov_fee)) * (1.0 + id(vat_rate)); const double MULT_POS = (1.0 + id(prov_fee)) * (1.0 + id(vat_rate));
const double MULT_NEG = (1.0 - id(neg_prov_fee)) * (1.0 + id(vat_rate));
id(price_values_tomorrow).clear(); id(price_values_tomorrow).clear();
id(price_timestamps_tomorrow).clear(); id(price_timestamps_tomorrow).clear();
@@ -1437,7 +1452,7 @@ script:
if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) continue; if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) continue;
if (unix_ts <= 0) continue; if (unix_ts <= 0) continue;
float eur_kwh = (float)((double)raw_mwh * MULT / 1000.0); float eur_kwh = (float)((double)raw_mwh * (raw_mwh >= 0 ? MULT_POS : MULT_NEG) / 1000.0);
id(price_timestamps_tomorrow).push_back(unix_ts); id(price_timestamps_tomorrow).push_back(unix_ts);
id(price_values_tomorrow).push_back(eur_kwh); id(price_values_tomorrow).push_back(eur_kwh);
} }
+107
View File
@@ -0,0 +1,107 @@
# =============================================================================
# EPrices Template secrets file
# Copy this file to your ESPHome config directory as "secrets.yaml"
# and fill in your own values.
#
# Notes on value formats:
# - All values are strings in secrets.yaml.
# - Numeric values (prov_fee, vat_rate) must use a DECIMAL DOT (e.g. 0.12),
# not a decimal comma. They are loaded as substitutions and passed directly
# into C++ double globals, so the dot is mandatory.
# =============================================================================
# --- WiFi credentials (shared with other ESPHome devices - remove if already present) ---
wifi_ssid: "your_wifi_ssid"
wifi_password: "your_wifi_password"
# --- EPrices fallback AP (shown when WiFi is unavailable) ---
eprices_fallback_ap_ssid: "EPrices-Fallback"
eprices_fallback_ap_password: "your_fallback_ap_password"
# --- ESPHome API encryption key (generate with: esphome generate-api-key) ---
eprices_api_encryption_key: "your_base64_api_encryption_key_here"
# --- Timezone string (IANA format, must match your location) ---
# Examples: "Europe/Ljubljana", "Europe/Berlin", "Europe/London"
eprices_timezone: "Europe/Ljubljana"
# --- Bidding zone for Energy-Charts API ---
# This determines which country's spot prices are fetched.
# Examples: "SI" (Slovenia), "DE-LU" (Germany/Luxembourg), "AT" (Austria),
# "FR" (France), "IT-North" (Italy North), "HR" (Croatia)
# Full list: https://api.energy-charts.info/
eprices_country_bzn: "SI"
# --- Provider fee multiplier for POSITIVE spot prices ---
# Added on top of the raw spot price to account for your energy provider's fee.
# Value is a DECIMAL MULTIPLIER, not a percentage.
# Example: 0.12 means 12% provider fee added to the spot price.
# Use decimal DOT, not comma (correct: 0.12 | wrong: 0,12).
eprices_prov_fee: "0.12"
# --- Provider fee multiplier for NEGATIVE spot prices ---
# When the market spot price is negative, the provider pays you a share of
# the negative price. This value is the fraction the provider keeps.
# Example: 0.30 means provider keeps 30%, pays you 70% of the negative price.
# Example: 0.00 means provider passes the full negative price to you (no fee).
# Use decimal DOT, not comma (correct: 0.30 | wrong: 0,30).
eprices_neg_prov_fee: "0.30"
# --- VAT rate multiplier ---
# Value added to the price to account for Value Added Tax in your country.
# Value is a DECIMAL MULTIPLIER, not a percentage.
# Example: 0.22 means 22% VAT (Slovenia). Germany is 0.19, Austria is 0.20.
# Use decimal DOT, not comma (correct: 0.22 | wrong: 0,22).
eprices_vat_rate: "0.22"
# ==============================================================================
# --- ADDITIONAL INFO: Bidding zone list for Energy-Charts API on 2026-04-04 ---
# (Check https://api.energy-charts.info/ for potential future changes)
#
# AT - Austria (click to show all available bidding zones)
# BE - Belgium
# BG - Bulgaria
# CH - Switzerland
# CZ - Czech Republic
# DE-LU - Germany, Luxembourg
# DE-AT-LU - Germany, Austria, Luxembourg
# DK1 - Denmark 1
# DK2 - Denmark 2
# EE - Estionia
# ES - Spain
# FI - Finland
# FR - France
# GR - Greece
# HR - Croatia
# HU - Hungary
# IT-Calabria - Italy Calabria
# IT-Centre-North - Italy Centre North
# IT-Centre-South - Italy Centre South
# IT-North - Italy North
# IT-SACOAC - Italy Sardinia Corsica AC
# IT-SACODC - Italy Sardinia Corsica DC
# IT-Sardinia - Italy Sardinia
# IT-Sicily - Italy Sicily
# IT-South - Italy South
# LT - Lithuania
# LV - Latvia
# ME - Montenegro
# NL - Netherlands
# NO1 - Norway 1
# NO2 - Norway 2
# NO2NSL - Norway North Sea Link
# NO3 - Norway 3
# NO4 - Norway 4
# NO5 - Norway 5
# PL - Poland
# PT - Portugal
# RO - Romania
# RS - Serbia
# SE1 - Sweden 1
# SE2 - Sweden 2
# SE3 - Sweden 3
# SE4 - Sweden 4
# SI - Slovenia
# SK - Slovakia
# ==============================================================================