mirror of
https://github.com/Legolas-2025/EPrices.git
synced 2026-08-18 12:44:51 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
420a745129 | ||
|
|
70c0abcf9e | ||
|
|
8c68afdc80 | ||
|
|
951d99bca1 | ||
|
|
7cd38a500a | ||
|
|
a2ad24c017 | ||
|
|
007e31ae69 | ||
|
|
84f24700b8 | ||
|
|
7faaaceed0 | ||
|
|
6f3ba88394 | ||
|
|
134cb7bbb1 | ||
|
|
ee48ef13f4 | ||
|
|
a09de2a71b | ||
|
|
fa507ff8ef |
+182
-10
@@ -1,4 +1,175 @@
|
||||
# EPrices v1.0 – Changelog
|
||||
# EPrices – Changelog
|
||||
|
||||
## v1.2.1 — 2026-04-07
|
||||
|
||||
### ESP32 main task stack size increase
|
||||
|
||||
Increased the FreeRTOS main application task stack from the ESP-IDF default
|
||||
of 8192 bytes to 16384 bytes. This eliminates the stack overflow scenario
|
||||
that most likely caused the spontaneous reboot observed in production on
|
||||
2026-04-06, where the FreeRTOS idle task faulted following memory pressure
|
||||
during a simultaneous NVS load and HTTP fetch/parse cycle.
|
||||
|
||||
The 8 KB cost is well within the ESP32's 320 KB available RAM.
|
||||
|
||||
**Changed location in `eprices.yaml`:**
|
||||
- `esp32: framework: sdkconfig_options:` — added
|
||||
`CONFIG_ESP_MAIN_TASK_STACK_SIZE: "16384"`
|
||||
|
||||
---
|
||||
|
||||
### Price vector heap pre-allocation at boot
|
||||
|
||||
Pre-allocated capacity for all four price vectors at boot using `.reserve(96)`.
|
||||
Previously, each vector started empty and grew incrementally on every NVS load
|
||||
and HTTP fetch, triggering repeated heap reallocations and leaving the heap
|
||||
fragmented before the first fetch cycle completed.
|
||||
|
||||
With capacity reserved upfront, no reallocation occurs during any subsequent
|
||||
NVS load or HTTP parse operation. This reduces heap fragmentation and peak
|
||||
allocation spikes, particularly during the boot sequence where NVS load and
|
||||
the first HTTP fetch can overlap.
|
||||
|
||||
**Vectors pre-allocated:**
|
||||
- `price_timestamps_today`
|
||||
- `price_values_today`
|
||||
- `price_timestamps_tomorrow`
|
||||
- `price_values_tomorrow`
|
||||
|
||||
**Changed location in `eprices.yaml`:**
|
||||
- `on_boot:` lambda — added `.reserve(96)` on all four price vectors
|
||||
|
||||
---
|
||||
|
||||
### Hourly JSON sensor cleared state unified with 15-min sensors
|
||||
|
||||
The two hourly JSON text sensors previously published `"[]"` when cleared
|
||||
(at midnight bridge and on `clear_today_prices` / `clear_tomorrow_prices`).
|
||||
The six 15-minute JSON sensors already published `""` in the same situation.
|
||||
All eight JSON sensors now publish `""` when cleared, giving consistent
|
||||
behaviour across the full sensor set.
|
||||
|
||||
**Affected sensors:**
|
||||
- `Today JSON Hourly Prices EUR⁄kWh`
|
||||
- `Tomorrow JSON Hourly Prices EUR⁄kWh`
|
||||
|
||||
**Changed locations in `eprices.yaml`:**
|
||||
- `clear_today_prices` script — `"[]"` → `""`
|
||||
- `clear_tomorrow_prices` script — `"[]"` → `""`
|
||||
- `midnight_bridge_promotion` script — `"[]"` → `""` (where applicable)
|
||||
|
||||
---
|
||||
|
||||
## v1.2 — 2026-04-06
|
||||
|
||||
### HTTP fetch stuck-flag watchdog
|
||||
|
||||
Fixed a reliability issue where an HTTP fetch that stalled at the TCP level
|
||||
(connection accepted by the server but data delivery suspended) could hold
|
||||
`is_updating_today` or `is_updating_tomorrow` locked at `true` indefinitely.
|
||||
While the flag was stuck, all subsequent auto-retry triggers and manual button
|
||||
presses were silently blocked.
|
||||
|
||||
**Root cause:** The application-level 25 s HTTP timeout does not fire when the
|
||||
TCP connection is established but the server stops sending data mid-transfer.
|
||||
The socket never goes idle from the OS perspective, so the timeout never
|
||||
triggers, and the fetch hangs until the TCP stack eventually resets the
|
||||
connection — which can take several minutes.
|
||||
|
||||
**Fix:** Added a 120-second watchdog to both the today and tomorrow worker
|
||||
loops (`seconds: /10`). If `is_updating_*` has been `true` for more than 120
|
||||
seconds, the worker force-clears the flag and sets the status message to
|
||||
`"Fetch timeout – will retry"`. The next scheduled retry or manual press then
|
||||
proceeds normally.
|
||||
|
||||
**New globals:**
|
||||
- `is_updating_today_since` (`time_t`) — timestamp when `is_updating_today` was last set to `true`
|
||||
- `is_updating_tomorrow_since` (`time_t`) — timestamp when `is_updating_tomorrow` was last set to `true`
|
||||
|
||||
Both timestamps are set immediately alongside `is_updating_* = true` inside
|
||||
`smart_price_update` and `smart_tomorrow_price_update`.
|
||||
|
||||
**Changed locations in `eprices.yaml`:**
|
||||
- `globals:` — added `is_updating_today_since` and `is_updating_tomorrow_since`
|
||||
- `smart_price_update` script — added `id(is_updating_today_since) = ...` alongside flag set
|
||||
- `smart_tomorrow_price_update` script — added `id(is_updating_tomorrow_since) = ...` alongside flag set
|
||||
- Today worker (`seconds: /10`) — watchdog block added before existing worker logic
|
||||
- Tomorrow worker (`seconds: /10`) — watchdog block added before existing worker logic
|
||||
|
||||
---
|
||||
|
||||
### Tomorrow auto-retry attempt counter fix
|
||||
|
||||
Fixed a bug where `tomorrow_retry_count` was not incremented in the 13:55 and
|
||||
14:55–19:55 hourly retry triggers. Those triggers called
|
||||
`smart_tomorrow_price_update` directly without incrementing the counter first,
|
||||
causing the `Tomorrow API Fetch Attempts` diagnostic sensor to undercount
|
||||
attempts after the first one at 13:25.
|
||||
|
||||
**Changed locations in `eprices.yaml`:**
|
||||
- 13:55 trigger — added `id(tomorrow_retry_count)++` before `.execute()`
|
||||
- 14:55–19:55 hourly trigger — added `id(tomorrow_retry_count)++` before `.execute()`
|
||||
|
||||
---
|
||||
|
||||
### Status message improvements
|
||||
|
||||
Two status message strings that contained a hardcoded time reference have been
|
||||
corrected. Both were misleading after a device reboot in the afternoon or a
|
||||
manual `clear_tomorrow_prices` call outside the normal fetch window.
|
||||
|
||||
| Location | Old value | New value |
|
||||
|---|---|---|
|
||||
| `tomorrow_update_status_message` global `initial_value` | `"Waiting for 13:20"` | `"No data yet"` |
|
||||
| `clear_tomorrow_prices` script end | `"Cleared – awaiting next 13:20 window"` | `"Cleared – awaiting fetch window"` |
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
@@ -68,10 +239,10 @@ eprices_vat_rate # e.g. "0.22" (VAT rate as decimal multiplier)
|
||||
|
||||
| Name | Entity ID | Notes |
|
||||
|---|---|---|
|
||||
| Today JSON Hourly Prices EUR⁄kWh | `sensor.eprices_today_json_hourly_prices_eur_kwh` | JSON array, 24 values |
|
||||
| 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, 32 values |
|
||||
| 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, 32 values |
|
||||
| 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, 32 values |
|
||||
| Today JSON Hourly Prices EUR⁄kWh | `sensor.eprices_today_json_hourly_prices_eur_kwh` | JSON array, 24 values; `""` 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, 32 values; `""` 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, 32 values; `""` 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, 32 values; `""` when no data |
|
||||
| Today Highest Price Time | `sensor.eprices_today_highest_price_time` | HH:MM |
|
||||
| Today Lowest Price Time | `sensor.eprices_today_lowest_price_time` | HH:MM |
|
||||
| Today Highest Hourly Price Time | `sensor.eprices_today_highest_hourly_price_time` | HH:00 |
|
||||
@@ -83,10 +254,10 @@ eprices_vat_rate # e.g. "0.22" (VAT rate as decimal multiplier)
|
||||
| Today Price Update Status Message | `sensor.eprices_today_price_update_status_message` | Detailed status string; diagnostic |
|
||||
| Today API Fetch Attempts | `sensor.eprices_today_api_fetch_attempts` | HTTP fetch count; resets at midnight; diagnostic |
|
||||
| Today Entry Count | `sensor.eprices_today_entry_count` | Number of stored price points; diagnostic |
|
||||
| Tomorrow JSON Hourly Prices EUR⁄kWh | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` | JSON array, 24 values |
|
||||
| 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, 32 values |
|
||||
| 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, 32 values |
|
||||
| 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, 32 values |
|
||||
| Tomorrow JSON Hourly Prices EUR⁄kWh | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` | JSON array, 24 values; `""` 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, 32 values; `""` 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, 32 values; `""` 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, 32 values; `""` when no data |
|
||||
| Tomorrow Highest Price Time | `sensor.eprices_tomorrow_highest_price_time` | HH:MM |
|
||||
| Tomorrow Lowest Price Time | `sensor.eprices_tomorrow_lowest_price_time` | HH:MM |
|
||||
| Tomorrow Highest Hourly Price Time | `sensor.eprices_tomorrow_highest_hourly_price_time` | HH:00 |
|
||||
@@ -126,4 +297,5 @@ eprices_vat_rate # e.g. "0.22" (VAT rate as decimal multiplier)
|
||||
- **Tomorrow Data Loaded Time** shows `Outside fetch window` when device boots before 13:20
|
||||
- **API fetch times** reset to `Never` at midnight bridge and on `clear_tomorrow_prices`
|
||||
- **API fetch attempt counters** reset to `0` at midnight and publish `0` immediately on boot
|
||||
- **Uptime** displayed as human-readable string: `45 s` / `5 min` / `3 h 22 min` / `12 d 4 h` / `4 months 12 d`
|
||||
- **Uptime** displayed as human-readable string: `45 s` / `5 min` / `3 h 22 min` / `12 d 4 h` / `4 months 12 d`
|
||||
- **JSON sensors** publish `""` (empty string) when no data is available — all eight JSON sensors behave consistently
|
||||
|
||||
+103
-77
@@ -85,96 +85,114 @@ The goal of EPrices was to:
|
||||
| Manual force update | Button | Button (unchanged) |
|
||||
| Midnight bridge | External HA automation at 00:00 | On-device `on_time: 00:00:00` |
|
||||
|
||||
---
|
||||
|
||||
### Sensor naming
|
||||
|
||||
All sensors were renamed with a systematic **Today / Tomorrow** prefix.
|
||||
The words "electricity" and "energy" were removed from all sensor names.
|
||||
"Next Day" was replaced with "Tomorrow" throughout.
|
||||
|
||||
> **Note on entity ID slugs:** HA derives entity IDs by lower-casing the
|
||||
> sensor name and replacing spaces and special characters with underscores.
|
||||
> The `⁄` character in JSON sensor names may have been rendered differently
|
||||
> by older ESPHome versions — verify those specific IDs against your actual
|
||||
> HA instance if needed.
|
||||
|
||||
---
|
||||
|
||||
#### Numeric sensors
|
||||
|
||||
| entso-e-prices v4.3.1 name | EPrices v1.0 name |
|
||||
|---|---|
|
||||
| `Current Electricity Price` | `Today Current Price` |
|
||||
| `Next Electricity Price` | `Today Next Price` |
|
||||
| `Average Electricity Price Today` | `Today Average Price` |
|
||||
| `Highest Electricity Price Today` | `Today Highest Price` |
|
||||
| `Lowest Electricity Price Today` | `Today Lowest Price` |
|
||||
| `Current Hourly Electricity Price` | `Today Current Hourly Price` |
|
||||
| `Next Hourly Electricity Price` | `Today Next Hourly Price` |
|
||||
| `Highest Hourly Electricity Price Today` | `Today Highest Hourly Price` |
|
||||
| `Lowest Hourly Electricity Price Today` | `Today Lowest Hourly Price` |
|
||||
| `Current Max Hourly Price Percentage` | `Today Current Max Hourly Price Percentage` |
|
||||
| `Daily Price Update Attempts` | `Today API Fetch Attempts` *(moved to text_sensor)* |
|
||||
| `Today Entry Count` | `Today Entry Count` *(moved to text_sensor)* |
|
||||
| `Next Day Current Electricity Price` | `Tomorrow Current Price` |
|
||||
| `Next Day Next Electricity Price` | `Tomorrow Next Price` |
|
||||
| `Average Electricity Price Tomorrow` | `Tomorrow Average Price` |
|
||||
| `Highest Electricity Price Tomorrow` | `Tomorrow Highest Price` |
|
||||
| `Lowest Electricity Price Tomorrow` | `Tomorrow Lowest Price` |
|
||||
| `Current Hourly Electricity Price Tomorrow` | `Tomorrow Current Hourly Price` |
|
||||
| `Next Hourly Electricity Price Tomorrow` | `Tomorrow Next Hourly Price` |
|
||||
| `Highest Hourly Electricity Price Tomorrow` | `Tomorrow Highest Hourly Price` |
|
||||
| `Lowest Hourly Electricity Price Tomorrow` | `Tomorrow Lowest Hourly Price` |
|
||||
| `Next Day Current Max Hourly Price Percentage` | `Tomorrow Current Max Hourly Price Percentage` |
|
||||
| `Next Day Price Update Attempts` | `Tomorrow API Fetch Attempts` *(moved to text_sensor)* |
|
||||
| `Tomorrow Entry Count` | `Tomorrow Entry Count` *(moved to text_sensor)* |
|
||||
| entso-e-prices v4.3.1 name | v4.3.1 entity ID | EPrices v1.2 name | EPrices v1.2 entity ID |
|
||||
|---|---|---|---|
|
||||
| `Current Electricity Price` | `sensor.entso_e_prices_current_electricity_price` | `Today Current Price` | `sensor.eprices_today_current_price` |
|
||||
| `Next Electricity Price` | `sensor.entso_e_prices_next_electricity_price` | `Today Next Price` | `sensor.eprices_today_next_price` |
|
||||
| `Average Electricity Price Today` | `sensor.entso_e_prices_average_electricity_price_today` | `Today Average Price` | `sensor.eprices_today_average_price` |
|
||||
| `Highest Electricity Price Today` | `sensor.entso_e_prices_highest_electricity_price_today` | `Today Highest Price` | `sensor.eprices_today_highest_price` |
|
||||
| `Lowest Electricity Price Today` | `sensor.entso_e_prices_lowest_electricity_price_today` | `Today Lowest Price` | `sensor.eprices_today_lowest_price` |
|
||||
| `Current Hourly Electricity Price` | `sensor.entso_e_prices_current_hourly_electricity_price` | `Today Current Hourly Price` | `sensor.eprices_today_current_hourly_price` |
|
||||
| `Next Hourly Electricity Price` | `sensor.entso_e_prices_next_hourly_electricity_price` | `Today Next Hourly Price` | `sensor.eprices_today_next_hourly_price` |
|
||||
| `Highest Hourly Electricity Price Today` | `sensor.entso_e_prices_highest_hourly_electricity_price_today` | `Today Highest Hourly Price` | `sensor.eprices_today_highest_hourly_price` |
|
||||
| `Lowest Hourly Electricity Price Today` | `sensor.entso_e_prices_lowest_hourly_electricity_price_today` | `Today Lowest Hourly Price` | `sensor.eprices_today_lowest_hourly_price` |
|
||||
| `Current Max Hourly Price Percentage` | `sensor.entso_e_prices_current_max_hourly_price_percentage` | `Today Current Max Hourly Price Percentage` | `sensor.eprices_today_current_max_hourly_price_percentage` |
|
||||
| `Daily Price Update Attempts` | `sensor.entso_e_prices_daily_price_update_attempts` | `Today API Fetch Attempts` *(moved to text_sensor)* | `sensor.eprices_today_api_fetch_attempts` |
|
||||
| `Today Entry Count` | `sensor.entso_e_prices_today_entry_count` | `Today Entry Count` *(moved to text_sensor)* | `sensor.eprices_today_entry_count` |
|
||||
| `Next Day Current Electricity Price` | `sensor.entso_e_prices_next_day_current_electricity_price` | `Tomorrow Current Price` | `sensor.eprices_tomorrow_current_price` |
|
||||
| `Next Day Next Electricity Price` | `sensor.entso_e_prices_next_day_next_electricity_price` | `Tomorrow Next Price` | `sensor.eprices_tomorrow_next_price` |
|
||||
| `Average Electricity Price Tomorrow` | `sensor.entso_e_prices_average_electricity_price_tomorrow` | `Tomorrow Average Price` | `sensor.eprices_tomorrow_average_price` |
|
||||
| `Highest Electricity Price Tomorrow` | `sensor.entso_e_prices_highest_electricity_price_tomorrow` | `Tomorrow Highest Price` | `sensor.eprices_tomorrow_highest_price` |
|
||||
| `Lowest Electricity Price Tomorrow` | `sensor.entso_e_prices_lowest_electricity_price_tomorrow` | `Tomorrow Lowest Price` | `sensor.eprices_tomorrow_lowest_price` |
|
||||
| `Current Hourly Electricity Price Tomorrow` | `sensor.entso_e_prices_current_hourly_electricity_price_tomorrow` | `Tomorrow Current Hourly Price` | `sensor.eprices_tomorrow_current_hourly_price` |
|
||||
| `Next Hourly Electricity Price Tomorrow` | `sensor.entso_e_prices_next_hourly_electricity_price_tomorrow` | `Tomorrow Next Hourly Price` | `sensor.eprices_tomorrow_next_hourly_price` |
|
||||
| `Highest Hourly Electricity Price Tomorrow` | `sensor.entso_e_prices_highest_hourly_electricity_price_tomorrow` | `Tomorrow Highest Hourly Price` | `sensor.eprices_tomorrow_highest_hourly_price` |
|
||||
| `Lowest Hourly Electricity Price Tomorrow` | `sensor.entso_e_prices_lowest_hourly_electricity_price_tomorrow` | `Tomorrow Lowest Hourly Price` | `sensor.eprices_tomorrow_lowest_hourly_price` |
|
||||
| `Next Day Current Max Hourly Price Percentage` | `sensor.entso_e_prices_next_day_current_max_hourly_price_percentage` | `Tomorrow Current Max Hourly Price Percentage` | `sensor.eprices_tomorrow_current_max_hourly_price_percentage` |
|
||||
| `Next Day Price Update Attempts` | `sensor.entso_e_prices_next_day_price_update_attempts` | `Tomorrow API Fetch Attempts` *(moved to text_sensor)* | `sensor.eprices_tomorrow_api_fetch_attempts` |
|
||||
| `Tomorrow Entry Count` | `sensor.entso_e_prices_tomorrow_entry_count` | `Tomorrow Entry Count` *(moved to text_sensor)* | `sensor.eprices_tomorrow_entry_count` |
|
||||
|
||||
---
|
||||
|
||||
#### Text sensors
|
||||
|
||||
| entso-e-prices v4.3.1 name | EPrices v1.0 name |
|
||||
|---|---|
|
||||
| `ENTSO-E Hourly Prices EUR⁄kWh JSON` | `Today JSON Hourly Prices EUR⁄kWh` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P1 00:00-07:45)` | `Today JSON 15-Min Prices EUR⁄kWh (P1 00:00-07:45)` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P2 08:00-15:45)` | `Today JSON 15-Min Prices EUR⁄kWh (P2 08:00-15:45)` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P3 16:00-23:45)` | `Today JSON 15-Min Prices EUR⁄kWh (P3 16:00-23:45)` |
|
||||
| `Time Of Highest Energy Price Today` | `Today Highest Price Time` |
|
||||
| `Time Of Lowest Energy Price Today` | `Today Lowest Price Time` |
|
||||
| `Time Of Highest Hourly Energy Price Today` | `Today Highest Hourly Price Time` |
|
||||
| `Time Of Lowest Hourly Energy Price Today` | `Today Lowest Hourly Price Time` |
|
||||
| `Price Update Status` | `Today Price Update Status` |
|
||||
| `Last Price Update Time` | `Today Data Loaded Time` |
|
||||
| `Price Update Status Message` | `Today Price Update Status Message` |
|
||||
| `Current Price Status` | `Today Current Price Status` |
|
||||
| `ENTSO-E Next Day Hourly Prices EUR⁄kWh JSON` | `Tomorrow JSON Hourly Prices EUR⁄kWh` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P1 00:00-07:45)` | `Tomorrow JSON 15-Min Prices EUR⁄kWh (P1 00:00-07:45)` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P2 08:00-15:45)` | `Tomorrow JSON 15-Min Prices EUR⁄kWh (P2 08:00-15:45)` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P3 16:00-23:45)` | `Tomorrow JSON 15-Min Prices EUR⁄kWh (P3 16:00-23:45)` |
|
||||
| `Time Of Highest Energy Price Tomorrow` | `Tomorrow Highest Price Time` |
|
||||
| `Time Of Lowest Energy Price Tomorrow` | `Tomorrow Lowest Price Time` |
|
||||
| `Time Of Highest Hourly Energy Price Tomorrow` | `Tomorrow Highest Hourly Price Time` |
|
||||
| `Time Of Lowest Hourly Energy Price Tomorrow` | `Tomorrow Lowest Hourly Price Time` |
|
||||
| `Next Day Price Update Status` | `Tomorrow Price Update Status` |
|
||||
| `Next Day Last Price Update Time` | `Tomorrow Data Loaded Time` |
|
||||
| `Next Day Price Update Status Message` | `Tomorrow Price Update Status Message` |
|
||||
| `Next Day Current Price Status` | `Tomorrow Current Price Status` |
|
||||
| `ENTSO-E Last Reboot` | `Last Reboot` |
|
||||
| `Entso-E Today NVS Status` | `Today NVS Status` |
|
||||
| `Entso-E Tomorrow NVS Status` | `Tomorrow NVS Status` |
|
||||
| `ENTSO-E Last Update Source` | `Last Update Source` |
|
||||
| `Today Data Date` | `Today Data Date` *(unchanged)* |
|
||||
| `Tomorrow Data Date` | `Tomorrow Data Date` *(unchanged)* |
|
||||
| entso-e-prices v4.3.1 name | v4.3.1 entity ID | EPrices v1.2 name | EPrices v1.2 entity ID |
|
||||
|---|---|---|---|
|
||||
| `ENTSO-E Hourly Prices EUR⁄kWh JSON` | `sensor.entso_e_prices_entso_e_hourly_prices_eur_kwh_json` | `Today JSON Hourly Prices EUR⁄kWh` | `sensor.eprices_today_json_hourly_prices_eur_kwh` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P1 00:00-07:45)` | `sensor.entso_e_prices_entso_e_15_min_prices_eur_kwh_json_p1_00_00_07_45` | `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` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P2 08:00-15:45)` | `sensor.entso_e_prices_entso_e_15_min_prices_eur_kwh_json_p2_08_00_15_45` | `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` |
|
||||
| `ENTSO-E 15-Min Prices EUR⁄kWh JSON (P3 16:00-23:45)` | `sensor.entso_e_prices_entso_e_15_min_prices_eur_kwh_json_p3_16_00_23_45` | `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` |
|
||||
| `Time Of Highest Energy Price Today` | `sensor.entso_e_prices_time_of_highest_energy_price_today` | `Today Highest Price Time` | `sensor.eprices_today_highest_price_time` |
|
||||
| `Time Of Lowest Energy Price Today` | `sensor.entso_e_prices_time_of_lowest_energy_price_today` | `Today Lowest Price Time` | `sensor.eprices_today_lowest_price_time` |
|
||||
| `Time Of Highest Hourly Energy Price Today` | `sensor.entso_e_prices_time_of_highest_hourly_energy_price_today` | `Today Highest Hourly Price Time` | `sensor.eprices_today_highest_hourly_price_time` |
|
||||
| `Time Of Lowest Hourly Energy Price Today` | `sensor.entso_e_prices_time_of_lowest_hourly_energy_price_today` | `Today Lowest Hourly Price Time` | `sensor.eprices_today_lowest_hourly_price_time` |
|
||||
| `Price Update Status` | `sensor.entso_e_prices_price_update_status` | `Today Price Update Status` | `sensor.eprices_today_price_update_status` |
|
||||
| `Last Price Update Time` | `sensor.entso_e_prices_last_price_update_time` | `Today Data Loaded Time` | `sensor.eprices_today_data_loaded_time` |
|
||||
| `Price Update Status Message` | `sensor.entso_e_prices_price_update_status_message` | `Today Price Update Status Message` | `sensor.eprices_today_price_update_status_message` |
|
||||
| `Current Price Status` | `sensor.entso_e_prices_current_price_status` | `Today Current Price Status` | `sensor.eprices_today_current_price_status` |
|
||||
| `ENTSO-E Next Day Hourly Prices EUR⁄kWh JSON` | `sensor.entso_e_prices_entso_e_next_day_hourly_prices_eur_kwh_json` | `Tomorrow JSON Hourly Prices EUR⁄kWh` | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P1 00:00-07:45)` | `sensor.entso_e_prices_entso_e_next_day_15_min_prices_eur_kwh_json_p1_00_00_07_45` | `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` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P2 08:00-15:45)` | `sensor.entso_e_prices_entso_e_next_day_15_min_prices_eur_kwh_json_p2_08_00_15_45` | `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` |
|
||||
| `ENTSO-E Next Day 15-Min Prices EUR⁄kWh JSON (P3 16:00-23:45)` | `sensor.entso_e_prices_entso_e_next_day_15_min_prices_eur_kwh_json_p3_16_00_23_45` | `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` |
|
||||
| `Time Of Highest Energy Price Tomorrow` | `sensor.entso_e_prices_time_of_highest_energy_price_tomorrow` | `Tomorrow Highest Price Time` | `sensor.eprices_tomorrow_highest_price_time` |
|
||||
| `Time Of Lowest Energy Price Tomorrow` | `sensor.entso_e_prices_time_of_lowest_energy_price_tomorrow` | `Tomorrow Lowest Price Time` | `sensor.eprices_tomorrow_lowest_price_time` |
|
||||
| `Time Of Highest Hourly Energy Price Tomorrow` | `sensor.entso_e_prices_time_of_highest_hourly_energy_price_tomorrow` | `Tomorrow Highest Hourly Price Time` | `sensor.eprices_tomorrow_highest_hourly_price_time` |
|
||||
| `Time Of Lowest Hourly Energy Price Tomorrow` | `sensor.entso_e_prices_time_of_lowest_hourly_energy_price_tomorrow` | `Tomorrow Lowest Hourly Price Time` | `sensor.eprices_tomorrow_lowest_hourly_price_time` |
|
||||
| `Next Day Price Update Status` | `sensor.entso_e_prices_next_day_price_update_status` | `Tomorrow Price Update Status` | `sensor.eprices_tomorrow_price_update_status` |
|
||||
| `Next Day Last Price Update Time` | `sensor.entso_e_prices_next_day_last_price_update_time` | `Tomorrow Data Loaded Time` | `sensor.eprices_tomorrow_data_loaded_time` |
|
||||
| `Next Day Price Update Status Message` | `sensor.entso_e_prices_next_day_price_update_status_message` | `Tomorrow Price Update Status Message` | `sensor.eprices_tomorrow_price_update_status_message` |
|
||||
| `Next Day Current Price Status` | `sensor.entso_e_prices_next_day_current_price_status` | `Tomorrow Current Price Status` | `sensor.eprices_tomorrow_current_price_status` |
|
||||
| `ENTSO-E Last Reboot` | `sensor.entso_e_prices_entso_e_last_reboot` | `Last Reboot` | `sensor.eprices_last_reboot` |
|
||||
| `Entso-E Today NVS Status` | `sensor.entso_e_prices_entso_e_today_nvs_status` | `Today NVS Status` | `sensor.eprices_today_nvs_status` |
|
||||
| `Entso-E Tomorrow NVS Status` | `sensor.entso_e_prices_entso_e_tomorrow_nvs_status` | `Tomorrow NVS Status` | `sensor.eprices_tomorrow_nvs_status` |
|
||||
| `ENTSO-E Last Update Source` | `sensor.entso_e_prices_entso_e_last_update_source` | `Last Update Source` | `sensor.eprices_last_update_source` |
|
||||
| `Today Data Date` | `sensor.entso_e_prices_today_data_date` | `Today Data Date` *(unchanged)* | `sensor.eprices_today_data_date` |
|
||||
| `Tomorrow Data Date` | `sensor.entso_e_prices_tomorrow_data_date` | `Tomorrow Data Date` *(unchanged)* | `sensor.eprices_tomorrow_data_date` |
|
||||
|
||||
---
|
||||
|
||||
#### Buttons
|
||||
|
||||
| entso-e-prices v4.3.1 name | EPrices v1.0 name |
|
||||
|---|---|
|
||||
| `Entso-E Force Update` | `Force Today's Update` |
|
||||
| `Entso-E Force Next Day Update` | `Force Tomorrow's Update` |
|
||||
| `Entso-E Reboot Device` | `Reboot Device` |
|
||||
| entso-e-prices v4.3.1 name | v4.3.1 entity ID | EPrices v1.2 name | EPrices v1.2 entity ID |
|
||||
|---|---|---|---|
|
||||
| `Entso-E Force Update` | `button.entso_e_prices_entso_e_force_update` | `Force Today's Update` | `button.eprices_force_today_s_update` |
|
||||
| `Entso-E Force Next Day Update` | `button.entso_e_prices_entso_e_force_next_day_update` | `Force Tomorrow's Update` | `button.eprices_force_tomorrow_s_update` |
|
||||
| `Entso-E Reboot Device` | `button.entso_e_prices_entso_e_reboot_device` | `Reboot Device` | `button.eprices_reboot_device` |
|
||||
|
||||
---
|
||||
|
||||
### New sensors in EPrices v1.0 (no equivalent in v4.3.1)
|
||||
|
||||
| Sensor | Purpose |
|
||||
|---|---|
|
||||
| `Today Last API Fetch Time` | Timestamp of last successful HTTP fetch for today |
|
||||
| `Tomorrow Last API Fetch Time` | Timestamp of last successful HTTP fetch for tomorrow |
|
||||
| `WiFi Signal` | RSSI in dBm |
|
||||
| `Uptime` | Human-readable uptime string |
|
||||
| EPrices v1.2 name | EPrices v1.2 entity ID | Purpose |
|
||||
|---|---|---|
|
||||
| `Today Last API Fetch Time` | `sensor.eprices_today_last_api_fetch_time` | Timestamp of last successful HTTP fetch for today |
|
||||
| `Tomorrow Last API Fetch Time` | `sensor.eprices_tomorrow_last_api_fetch_time` | Timestamp of last successful HTTP fetch for tomorrow |
|
||||
| `WiFi Signal` | `sensor.eprices_wifi_signal` | RSSI in dBm |
|
||||
| `Uptime` | `sensor.eprices_uptime` | Human-readable uptime string |
|
||||
|
||||
---
|
||||
|
||||
### Sensor output changes
|
||||
|
||||
| Sensor | v4.3.1 output | EPrices v1.0 output |
|
||||
| Sensor | v4.3.1 output | EPrices v1.2 output |
|
||||
|---|---|---|
|
||||
| `Last Reboot` | `Last reboot: 2026-04-04 20:10:10` | `2026-04-04 20:10:10` |
|
||||
| `Today NVS Status` | `Today NVS: Stored 96 pts for 2026-04-04` | `Stored 96 pts for 2026-04-04` |
|
||||
@@ -186,23 +204,30 @@ The words "electricity" and "energy" were removed from all sensor names.
|
||||
| `Today Current Price Status` | `Valid` / `Missing` | `Valid` / `Missing` / `Stale` |
|
||||
| `Today Data Loaded Time` | Stamped on HTTP fetch only | Stamped on NVS load and HTTP fetch |
|
||||
| `Tomorrow Data Loaded Time` | `Never` after NVS boot load | Stamped on NVS load and HTTP fetch |
|
||||
| `Tomorrow Price Update Status Message` | *(various)* | `"No data yet"` on boot / after reboot (v1.2) |
|
||||
|
||||
---
|
||||
|
||||
### HA entity ID changes
|
||||
|
||||
All entity IDs changed due to the node name change from `entso-e-prices` to `eprices`.
|
||||
|
||||
**Find and replace in all your automations and dashboards:**
|
||||
**Step 1 — bulk find and replace in all your automations and dashboards:**
|
||||
```
|
||||
sensor.entso_e_prices_ → sensor.eprices_
|
||||
button.entso_e_prices_ → button.eprices_
|
||||
```
|
||||
|
||||
Then apply the individual sensor name slug changes from the tables above.
|
||||
**Step 2 — apply the individual sensor name slug renames** from the tables above
|
||||
for sensors whose names changed (not just the prefix).
|
||||
|
||||
> **Tip:** After the bulk replace in Step 1, search for any remaining
|
||||
> `entso_e_prices_` strings to catch any that were missed.
|
||||
|
||||
### NVS data
|
||||
|
||||
The NVS namespace changed from `entsoe2` to `eprices`. On first boot after
|
||||
flashing EPrices v1.0 the device will not find any stored data and will
|
||||
flashing EPrices the device will not find any stored data and will
|
||||
trigger a fresh HTTP fetch automatically. This is expected and safe.
|
||||
No manual NVS erase is required.
|
||||
|
||||
@@ -211,9 +236,10 @@ No manual NVS erase is required.
|
||||
## Migration checklist
|
||||
|
||||
- [ ] Flash `eprices.yaml` to the device
|
||||
- [ ] Update `secrets.yaml` — rename all `entsoe_` keys to `eprices_`; add `eprices_prov_fee` and `eprices_vat_rate`
|
||||
- [ ] In all HA automations: replace `entso_e_prices_` with `eprices_` in all entity IDs
|
||||
- [ ] Apply individual sensor name slug renames from the tables above
|
||||
- [ ] Update `secrets.yaml` — rename all `entsoe_` keys to `eprices_`; add `eprices_prov_fee`, `eprices_vat_rate`, and `eprices_neg_prov_fee`
|
||||
- [ ] In all HA automations: bulk replace `sensor.entso_e_prices_` → `sensor.eprices_` and `button.entso_e_prices_` → `button.eprices_`
|
||||
- [ ] Apply individual sensor slug renames from the tables above for sensors whose names changed
|
||||
- [ ] Remove any external HA automations that handled midnight bridge, boot recovery, or fetch scheduling — these are now all on-device
|
||||
- [ ] Verify the device fetches fresh data on first boot (check `Today NVS Status` and `Today Price Update Status Message`)
|
||||
- [ ] Update any HA dashboard cards referencing old entity IDs
|
||||
- [ ] Update any HA dashboard cards referencing old entity IDs
|
||||
- [ ] Verify the JSON sensor entity IDs in dashboards — the `⁄` character slug may differ from expected; check against actual HA entity registry if needed
|
||||
|
||||
@@ -16,19 +16,21 @@ 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
|
||||
- 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
|
||||
- **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
|
||||
- **DST-safe** — uses UNIX timestamps and binary search throughout, no hour-slot arithmetic
|
||||
- **Staleness detection** — `Today 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
|
||||
- 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 +48,7 @@ Assistant automations are required for any core functionality.
|
||||
| `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 for v1.0 |
|
||||
| `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 |
|
||||
|
||||
@@ -71,6 +73,7 @@ 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:**
|
||||
@@ -79,8 +82,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_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_vat_rate` | VAT rate as a decimal multiplier, e.g. `"0.22"` = 22% |
|
||||
| `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
|
||||
@@ -130,14 +134,37 @@ For the full list see the [Energy-Charts API documentation](https://api.energy-c
|
||||
| 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:
|
||||
and applies your provider fee and VAT. Positive and negative spot prices use
|
||||
separate fee multipliers, configurable independently in `secrets.yaml`:
|
||||
|
||||
```
|
||||
price_eur_kwh = (raw_eur_mwh / 1000) × (1 + prov_fee) × (1 + vat_rate)
|
||||
```
|
||||
| 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
|
||||
|
||||
@@ -245,7 +272,7 @@ safe on DST transition days (23-hour and 25-hour days).
|
||||
| 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 |
|
||||
| Tomorrow Price Update Status Message | `sensor.eprices_tomorrow_price_update_status_message` | Detailed status string; `"Fetch timeout – will retry"` after watchdog trigger |
|
||||
|
||||
### Buttons
|
||||
|
||||
|
||||
+85
-1
@@ -1,5 +1,89 @@
|
||||
# EPrices – Version History
|
||||
|
||||
## v1.2.1 — 2026-04-07
|
||||
|
||||
Stability and housekeeping patch. No new sensors, no secrets changes,
|
||||
no entity ID changes. Drop-in replacement for v1.2.
|
||||
|
||||
### ESP32 main task stack size increase
|
||||
|
||||
Doubled the FreeRTOS main task stack from 8192 to 16384 bytes via
|
||||
`CONFIG_ESP_MAIN_TASK_STACK_SIZE`. Eliminates the stack overflow scenario
|
||||
most likely responsible for the spontaneous reboot observed in production
|
||||
on 2026-04-06 during a simultaneous NVS load + HTTP fetch/parse cycle.
|
||||
|
||||
### Price vector heap pre-allocation at boot
|
||||
|
||||
Added `.reserve(96)` on all four price vectors (`price_timestamps_today`,
|
||||
`price_values_today`, `price_timestamps_tomorrow`, `price_values_tomorrow`)
|
||||
in the `on_boot` lambda. Prevents repeated heap reallocation during NVS load
|
||||
and HTTP parse operations, reducing heap fragmentation and peak allocation
|
||||
pressure during the boot sequence.
|
||||
|
||||
### Hourly JSON sensor cleared state unified
|
||||
|
||||
`Today JSON Hourly Prices EUR⁄kWh` and `Tomorrow JSON Hourly Prices EUR⁄kWh`
|
||||
now publish `""` when cleared, matching the existing behaviour of all six
|
||||
15-minute JSON sensors. Previously they published `"[]"`.
|
||||
|
||||
See `CHANGELOG.md` for full implementation details.
|
||||
|
||||
---
|
||||
|
||||
## v1.2 — 2026-04-06
|
||||
|
||||
### HTTP fetch stuck-flag watchdog
|
||||
|
||||
Fixed a production-observed reliability issue where a TCP-level stall during
|
||||
an HTTP fetch could lock `is_updating_today` or `is_updating_tomorrow` at
|
||||
`true` for several minutes, silently blocking all auto-retry triggers and
|
||||
manual button presses for the duration.
|
||||
|
||||
A 120-second watchdog was added to both worker loops. If either `is_updating_*`
|
||||
flag has been held for more than 120 seconds, the worker force-clears it and
|
||||
sets the status message to `"Fetch timeout – will retry"`, allowing the next
|
||||
scheduled retry or manual press to proceed immediately.
|
||||
|
||||
New globals: `is_updating_today_since`, `is_updating_tomorrow_since`.
|
||||
|
||||
### Tomorrow auto-retry attempt counter fix
|
||||
|
||||
`tomorrow_retry_count` was not being incremented in the 13:55 and 14:55–19:55
|
||||
hourly retry triggers, causing the `Tomorrow API Fetch Attempts` diagnostic
|
||||
sensor to undercount after the first scheduled attempt at 13:25.
|
||||
|
||||
### Status message improvements
|
||||
|
||||
- `tomorrow_update_status_message` initial value changed from
|
||||
`"Waiting for 13:20"` to `"No data yet"`
|
||||
- `clear_tomorrow_prices` end message changed from
|
||||
`"Cleared – awaiting next 13:20 window"` to
|
||||
`"Cleared – awaiting fetch window"`
|
||||
|
||||
See `CHANGELOG.md` for full implementation details.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
First public release.
|
||||
@@ -49,4 +133,4 @@ persistently in ESP32 NVS flash so they survive reboots without re-fetching.
|
||||
| `secrets.yaml` | Local secrets — not committed to git |
|
||||
| `CHANGELOG.md` | Full sensor and entity ID reference for v1.0 |
|
||||
| `README.md` | Installation guide and full sensor reference |
|
||||
| `ENTSO-E-PRICES-MIGRATION.md` | Optional — migration guide from the predecessor project |
|
||||
| `ENTSO-E-PRICES-MIGRATION.md` | Optional — migration guide from the predecessor project |
|
||||
|
||||
+97
-19
@@ -1,13 +1,48 @@
|
||||
# =============================================================================
|
||||
# ESPHome: EPrices v1.0
|
||||
#
|
||||
# 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.
|
||||
# ESPHome: EPrices v1.2.1
|
||||
#
|
||||
# Electricity price data provided by Energy-Charts (https://energy-charts.info)
|
||||
# under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/).
|
||||
#
|
||||
# v1.2.1 changes (2026-04-07):
|
||||
# - CONFIG_ESP_MAIN_TASK_STACK_SIZE raised from 8192 to 16384 bytes via
|
||||
# esp32: framework: sdkconfig_options — eliminates stack overflow scenario
|
||||
# observed in production during simultaneous NVS load + HTTP fetch/parse
|
||||
# - Price vectors pre-allocated at boot with .reserve(96) in on_boot lambda:
|
||||
# price_timestamps_today, price_values_today,
|
||||
# price_timestamps_tomorrow, price_values_tomorrow
|
||||
# prevents repeated heap reallocation during NVS load and HTTP parse cycles
|
||||
# - Hourly JSON sensors now publish "" when cleared (was "[]") —
|
||||
# consistent with all six 15-min JSON sensors
|
||||
#
|
||||
# v1.2 changes (2026-04-06):
|
||||
# - HTTP fetch stuck-flag watchdog (120 s) added to today and tomorrow workers:
|
||||
# if is_updating_today / is_updating_tomorrow stays true for >120 s (TCP
|
||||
# stall not caught by app-level timeout), the flag is force-cleared and
|
||||
# status message set to "Fetch timeout – will retry"
|
||||
# - New globals: is_updating_today_since, is_updating_tomorrow_since
|
||||
# set alongside is_updating_* = true in smart_price_update and
|
||||
# smart_tomorrow_price_update
|
||||
# - tomorrow_retry_count++ added before .execute() in 13:55 and 14:55–19:55
|
||||
# hourly retry triggers (counter was not incrementing after attempt #1)
|
||||
# - tomorrow_update_status_message initial_value: "Waiting for 13:20"
|
||||
# → "No data yet" (removes misleading time reference after afternoon reboot)
|
||||
# - clear_tomorrow_prices end message: "Cleared – awaiting next 13:20 window"
|
||||
# → "Cleared – awaiting fetch window"
|
||||
#
|
||||
# v1.1 changes (2026-04-06):
|
||||
# - 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:
|
||||
# - Fetches 15-min and hourly spot prices for today and tomorrow
|
||||
# - Prices converted from raw €/MWh to €/kWh with provider fee and VAT applied
|
||||
@@ -33,6 +68,7 @@
|
||||
# eprices_country_bzn e.g. "SI"
|
||||
# eprices_prov_fee e.g. "0.12" (provider fee, decimal multiplier)
|
||||
# eprices_vat_rate e.g. "0.22" (VAT rate, decimal multiplier)
|
||||
# eprices_neg_prov_fee e.g. "0.30" (neg price provider fee, decimal multiplier)
|
||||
#
|
||||
# See README.md for full installation instructions and sensor reference.
|
||||
# See CHANGELOG.md for complete sensor and entity ID listing.
|
||||
@@ -40,9 +76,10 @@
|
||||
# =============================================================================
|
||||
|
||||
substitutions:
|
||||
country_bzn_value: !secret eprices_country_bzn
|
||||
prov_fee_value: !secret eprices_prov_fee
|
||||
vat_rate_value: !secret eprices_vat_rate
|
||||
country_bzn_value: !secret eprices_country_bzn
|
||||
prov_fee_value: !secret eprices_prov_fee
|
||||
vat_rate_value: !secret eprices_vat_rate
|
||||
neg_prov_fee_value: !secret eprices_neg_prov_fee
|
||||
|
||||
esphome:
|
||||
name: eprices
|
||||
@@ -60,11 +97,19 @@ esphome:
|
||||
} else {
|
||||
id(boot_time) = 0;
|
||||
}
|
||||
// Pre-allocate price vectors to avoid repeated heap reallocation
|
||||
// during NVS load and HTTP fetch/parse cycles
|
||||
id(price_timestamps_today).reserve(96);
|
||||
id(price_values_today).reserve(96);
|
||||
id(price_timestamps_tomorrow).reserve(96);
|
||||
id(price_values_tomorrow).reserve(96);
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
sdkconfig_options:
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE: "16384"
|
||||
# No include_builtin_idf_components needed — nvs headers are always
|
||||
# available in esp-idf framework; eprices_nvs.h includes them directly.
|
||||
|
||||
@@ -175,6 +220,15 @@ time:
|
||||
- seconds: /10
|
||||
then:
|
||||
- lambda: |-
|
||||
if (id(is_updating_today) && id(is_updating_today_since) > 0) {
|
||||
time_t now_ts = (time_t)id(ha_time).now().timestamp;
|
||||
if (now_ts - id(is_updating_today_since) > 120) {
|
||||
ESP_LOGW("eprices", "Worker: is_updating_today stuck >120s, force-clearing");
|
||||
id(is_updating_today) = false;
|
||||
id(is_updating_today_since) = 0;
|
||||
id(update_status_message) = "Fetch timeout – will retry";
|
||||
}
|
||||
}
|
||||
if (id(need_today_update) && !id(is_updating_today)) {
|
||||
ESP_LOGI("eprices", "Worker: starting Today update");
|
||||
id(need_today_update) = false;
|
||||
@@ -184,6 +238,15 @@ time:
|
||||
- seconds: /10
|
||||
then:
|
||||
- lambda: |-
|
||||
if (id(is_updating_tomorrow) && id(is_updating_tomorrow_since) > 0) {
|
||||
time_t now_ts = (time_t)id(ha_time).now().timestamp;
|
||||
if (now_ts - id(is_updating_tomorrow_since) > 120) {
|
||||
ESP_LOGW("eprices", "Worker: is_updating_tomorrow stuck >120s, force-clearing");
|
||||
id(is_updating_tomorrow) = false;
|
||||
id(is_updating_tomorrow_since) = 0;
|
||||
id(tomorrow_update_status_message) = "Fetch timeout – will retry";
|
||||
}
|
||||
}
|
||||
auto t = id(ha_time).now();
|
||||
bool in_window = (t.hour > 13 && t.hour < 23) ||
|
||||
(t.hour == 13 && t.minute >= 20) ||
|
||||
@@ -312,7 +375,8 @@ time:
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGW("eprices", "Auto tomorrow: attempt #%d at 13:55", id(tomorrow_retry_count) + 1);
|
||||
id(tomorrow_retry_count)++;
|
||||
ESP_LOGW("eprices", "Auto tomorrow: attempt #%d at 13:55", id(tomorrow_retry_count));
|
||||
id(smart_tomorrow_price_update).execute();
|
||||
|
||||
# Attempts #3.. at 14:55, 15:55, 16:55, 17:55, 18:55, 19:55 (capped to 8)
|
||||
@@ -333,7 +397,8 @@ time:
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGW("eprices", "Auto tomorrow: attempt #%d at %02d:55", id(tomorrow_retry_count) + 1, t.hour);
|
||||
id(tomorrow_retry_count)++;
|
||||
ESP_LOGW("eprices", "Auto tomorrow: attempt #%d at %02d:55", id(tomorrow_retry_count), t.hour);
|
||||
id(smart_tomorrow_price_update).execute();
|
||||
|
||||
- minutes: /1
|
||||
@@ -452,6 +517,9 @@ globals:
|
||||
- id: is_updating_today
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: is_updating_today_since
|
||||
type: time_t
|
||||
initial_value: '0'
|
||||
|
||||
- id: tomorrow_last_successful_update
|
||||
type: time_t
|
||||
@@ -468,7 +536,7 @@ globals:
|
||||
initial_value: 'false'
|
||||
- id: tomorrow_update_status_message
|
||||
type: std::string
|
||||
initial_value: '"Waiting for 13:20"'
|
||||
initial_value: '"No data yet"'
|
||||
- id: tomorrow_retry_count
|
||||
type: int
|
||||
initial_value: '0'
|
||||
@@ -484,6 +552,9 @@ globals:
|
||||
- id: is_updating_tomorrow
|
||||
type: bool
|
||||
initial_value: 'false'
|
||||
- id: is_updating_tomorrow_since
|
||||
type: time_t
|
||||
initial_value: '0'
|
||||
|
||||
# --- Country code for Energy-Charts API (wired from secret) ---
|
||||
- id: country_bzn
|
||||
@@ -497,6 +568,9 @@ globals:
|
||||
- id: vat_rate
|
||||
type: double
|
||||
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) ---
|
||||
- id: fetch_url_today
|
||||
@@ -1261,7 +1335,8 @@ script:
|
||||
raw_body: string
|
||||
then:
|
||||
- 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_timestamps_today).clear();
|
||||
@@ -1350,7 +1425,7 @@ script:
|
||||
if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) 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_values_today).push_back(eur_kwh);
|
||||
}
|
||||
@@ -1369,7 +1444,8 @@ script:
|
||||
raw_body: string
|
||||
then:
|
||||
- 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_timestamps_tomorrow).clear();
|
||||
@@ -1437,7 +1513,7 @@ script:
|
||||
if (end1 == ts_toks[i].c_str() || end2 == prc_toks[i].c_str()) 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_values_tomorrow).push_back(eur_kwh);
|
||||
}
|
||||
@@ -1754,6 +1830,7 @@ script:
|
||||
return;
|
||||
}
|
||||
id(is_updating_today) = true;
|
||||
id(is_updating_today_since) = (time_t)id(ha_time).now().timestamp;
|
||||
id(last_update_attempt) = id(ha_time).now().timestamp;
|
||||
id(retry_count)++;
|
||||
{ char buf[8]; snprintf(buf, sizeof(buf), "%d", id(retry_count)); id(today_price_update_attempts).publish_state(buf); }
|
||||
@@ -1857,6 +1934,7 @@ script:
|
||||
return;
|
||||
}
|
||||
id(is_updating_tomorrow) = true;
|
||||
id(is_updating_tomorrow_since) = (time_t)id(ha_time).now().timestamp;
|
||||
id(tomorrow_last_update_attempt) = id(ha_time).now().timestamp;
|
||||
id(tomorrow_retry_count)++;
|
||||
{ char buf[8]; snprintf(buf, sizeof(buf), "%d", id(tomorrow_retry_count)); id(tomorrow_price_update_attempts).publish_state(buf); }
|
||||
@@ -2044,7 +2122,7 @@ script:
|
||||
id(today_current_max_hourly_price_percentage).publish_state(NAN);
|
||||
id(last_update_success) = false;
|
||||
id(update_status_message) = "Cleared – awaiting NVS or HTTP";
|
||||
id(json_hourly_prices_kwh).publish_state("[]");
|
||||
id(json_hourly_prices_kwh).publish_state("");
|
||||
id(json_15min_prices_kwh_p1_00_00_07_45).publish_state("");
|
||||
id(json_15min_prices_kwh_p2_08_00_15_45).publish_state("");
|
||||
id(json_15min_prices_kwh_p3_16_00_23_45).publish_state("");
|
||||
@@ -2077,7 +2155,7 @@ script:
|
||||
id(tomorrow_min_hourly_price).publish_state(NAN);
|
||||
id(tomorrow_max_hourly_price).publish_state(NAN);
|
||||
id(tomorrow_current_max_hourly_price_percentage).publish_state(NAN);
|
||||
id(json_tomorrow_hourly_prices_kwh).publish_state("[]");
|
||||
id(json_tomorrow_hourly_prices_kwh).publish_state("");
|
||||
id(json_tomorrow_15min_prices_kwh_p1_00_00_07_45).publish_state("");
|
||||
id(json_tomorrow_15min_prices_kwh_p2_08_00_15_45).publish_state("");
|
||||
id(json_tomorrow_15min_prices_kwh_p3_16_00_23_45).publish_state("");
|
||||
@@ -2086,7 +2164,7 @@ script:
|
||||
id(tomorrow_min_hourly_price_time_str) = "--:--";
|
||||
id(tomorrow_max_hourly_price_time_str) = "--:--";
|
||||
id(tomorrow_last_update_success) = false;
|
||||
id(tomorrow_update_status_message) = "Cleared – awaiting next 13:20 window";
|
||||
id(tomorrow_update_status_message) = "Cleared – awaiting fetch window";
|
||||
id(tomorrow_current_price_status_str) = "Cleared";
|
||||
id(tomorrow_price_update_status).update();
|
||||
id(tomorrow_price_status_message).update();
|
||||
@@ -2094,4 +2172,4 @@ script:
|
||||
id(tomorrow_nvs_status).publish_state("Cleared");
|
||||
id(tomorrow_last_api_fetch_time) = std::string("Never");
|
||||
id(tomorrow_last_api_fetch_time_sensor).publish_state("Never");
|
||||
id(tomorrow_last_price_update_time).publish_state("Outside fetch window");
|
||||
id(tomorrow_last_price_update_time).publish_state("Outside fetch window");
|
||||
|
||||
+107
@@ -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
|
||||
# ==============================================================================
|
||||
|
||||
Reference in New Issue
Block a user