8 Commits
Author SHA1 Message Date
Amir 420a745129 Document version 1.2.1 release in VERSION.md
Added version 1.2.1 release notes detailing stability improvements, stack size increase, heap pre-allocation, and unified JSON sensor states.
2026-04-08 01:05:56 +02:00
Amir 70c0abcf9e Document changes for version 1.2.1
Updated changelog for version 1.2.1 with detailed changes including stack size increase, pre-allocation of price vectors, and unified JSON sensor states.
2026-04-08 01:05:09 +02:00
Amir 8c68afdc80 Bump version to 1.2.1 and improve price handling
Updated version to 1.2.1 and added changes for price vector pre-allocation and JSON sensor behavior.
2026-04-08 01:01:28 +02:00
Amir 951d99bca1 Update sensor names and migration instructions for EPrices
Renamed sensors and updated entity IDs for EPrices v1.2. Added migration checklist and clarified changes in sensor outputs and NVS data.
2026-04-06 21:50:03 +02:00
Amir 7cd38a500a Enhance README with stuck-fetch watchdog details (v1.2)
Added explanation for stuck-fetch watchdog mechanism and updated status message details.
2026-04-06 18:44:48 +02:00
Amir a2ad24c017 Document version 1.2 release notes in VERSION.md
Added version 1.2 release notes including fixes for HTTP fetch reliability, tomorrow auto-retry count, and status message improvements.
2026-04-06 18:42:21 +02:00
Amir 007e31ae69 Update changelog for v1.2 enhancements and fixes
Added a watchdog for HTTP fetch timeout, fixed tomorrow retry count, and improved status messages.
2026-04-06 18:41:33 +02:00
Amir 84f24700b8 Upgrade EPrices to version 1.2 with new features
Updated version from 1.1 to 1.2 with new features including HTTP fetch watchdog, new globals for tracking updates, and improved status messages.
2026-04-06 18:40:10 +02:00
5 changed files with 387 additions and 95 deletions
+134 -8
View File
@@ -1,5 +1,130 @@
# EPrices 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 EURkWh`
- `Tomorrow JSON Hourly Prices EURkWh`
**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:5519: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:5519: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 ## v1.1 — 2026-04-06
### Negative price provider fee support ### Negative price provider fee support
@@ -114,10 +239,10 @@ eprices_vat_rate # e.g. "0.22" (VAT rate as decimal multiplier)
| Name | Entity ID | Notes | | Name | Entity ID | Notes |
|---|---|---| |---|---|---|
| Today JSON Hourly Prices EURkWh | `sensor.eprices_today_json_hourly_prices_eur_kwh` | JSON array, 24 values | | Today JSON Hourly Prices EURkWh | `sensor.eprices_today_json_hourly_prices_eur_kwh` | JSON array, 24 values; `""` when no data |
| Today JSON 15-Min Prices EURkWh (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 EURkWh (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 EURkWh (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 EURkWh (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 EURkWh (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 15-Min Prices EURkWh (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 Highest Price Time | `sensor.eprices_today_highest_price_time` | HH:MM |
| Today Lowest Price Time | `sensor.eprices_today_lowest_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 | | Today Highest Hourly Price Time | `sensor.eprices_today_highest_hourly_price_time` | HH:00 |
@@ -129,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 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 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 | | Today Entry Count | `sensor.eprices_today_entry_count` | Number of stored price points; diagnostic |
| Tomorrow JSON Hourly Prices EURkWh | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` | JSON array, 24 values | | Tomorrow JSON Hourly Prices EURkWh | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` | JSON array, 24 values; `""` when no data |
| Tomorrow JSON 15-Min Prices EURkWh (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 EURkWh (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 EURkWh (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 EURkWh (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 EURkWh (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 15-Min Prices EURkWh (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 Highest Price Time | `sensor.eprices_tomorrow_highest_price_time` | HH:MM |
| Tomorrow Lowest Price Time | `sensor.eprices_tomorrow_lowest_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 | | Tomorrow Highest Hourly Price Time | `sensor.eprices_tomorrow_highest_hourly_price_time` | HH:00 |
@@ -173,3 +298,4 @@ eprices_vat_rate # e.g. "0.22" (VAT rate as decimal multiplier)
- **API fetch times** reset to `Never` at midnight bridge and on `clear_tomorrow_prices` - **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 - **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
+102 -76
View File
@@ -85,96 +85,114 @@ The goal of EPrices was to:
| Manual force update | Button | Button (unchanged) | | Manual force update | Button | Button (unchanged) |
| Midnight bridge | External HA automation at 00:00 | On-device `on_time: 00:00:00` | | Midnight bridge | External HA automation at 00:00 | On-device `on_time: 00:00:00` |
---
### Sensor naming ### Sensor naming
All sensors were renamed with a systematic **Today / Tomorrow** prefix. All sensors were renamed with a systematic **Today / Tomorrow** prefix.
The words "electricity" and "energy" were removed from all sensor names. The words "electricity" and "energy" were removed from all sensor names.
"Next Day" was replaced with "Tomorrow" throughout. "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 #### Numeric sensors
| entso-e-prices v4.3.1 name | EPrices v1.0 name | | entso-e-prices v4.3.1 name | v4.3.1 entity ID | EPrices v1.2 name | EPrices v1.2 entity ID |
|---|---| |---|---|---|---|
| `Current Electricity Price` | `Today Current Price` | | `Current Electricity Price` | `sensor.entso_e_prices_current_electricity_price` | `Today Current Price` | `sensor.eprices_today_current_price` |
| `Next Electricity Price` | `Today Next Price` | | `Next Electricity Price` | `sensor.entso_e_prices_next_electricity_price` | `Today Next Price` | `sensor.eprices_today_next_price` |
| `Average Electricity Price Today` | `Today Average 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` | `Today Highest 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` | `Today Lowest 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` | `Today Current Hourly 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` | `Today Next 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` | `Today Highest 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` | `Today Lowest 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` | `Today Current Max Hourly Price Percentage` | | `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` | `Today API Fetch Attempts` *(moved to text_sensor)* | | `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` | `Today Entry Count` *(moved to text_sensor)* | | `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` | `Tomorrow Current Price` | | `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` | `Tomorrow Next 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` | `Tomorrow Average 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` | `Tomorrow Highest 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` | `Tomorrow Lowest 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` | `Tomorrow Current Hourly 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` | `Tomorrow Next 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` | `Tomorrow Highest 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` | `Tomorrow Lowest 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` | `Tomorrow Current Max Hourly Price Percentage` | | `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` | `Tomorrow API Fetch Attempts` *(moved to text_sensor)* | | `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` | `Tomorrow Entry Count` *(moved to text_sensor)* | | `Tomorrow Entry Count` | `sensor.entso_e_prices_tomorrow_entry_count` | `Tomorrow Entry Count` *(moved to text_sensor)* | `sensor.eprices_tomorrow_entry_count` |
---
#### Text sensors #### Text sensors
| entso-e-prices v4.3.1 name | EPrices v1.0 name | | 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 EURkWh JSON` | `Today JSON Hourly Prices EURkWh` | | `ENTSO-E Hourly Prices EURkWh JSON` | `sensor.entso_e_prices_entso_e_hourly_prices_eur_kwh_json` | `Today JSON Hourly Prices EURkWh` | `sensor.eprices_today_json_hourly_prices_eur_kwh` |
| `ENTSO-E 15-Min Prices EURkWh JSON (P1 00:00-07:45)` | `Today JSON 15-Min Prices EURkWh (P1 00:00-07:45)` | | `ENTSO-E 15-Min Prices EURkWh 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 EURkWh (P1 00:00-07:45)` | `sensor.eprices_today_json_15_min_prices_eur_kwh_p1_00_00_07_45` |
| `ENTSO-E 15-Min Prices EURkWh JSON (P2 08:00-15:45)` | `Today JSON 15-Min Prices EURkWh (P2 08:00-15:45)` | | `ENTSO-E 15-Min Prices EURkWh 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 EURkWh (P2 08:00-15:45)` | `sensor.eprices_today_json_15_min_prices_eur_kwh_p2_08_00_15_45` |
| `ENTSO-E 15-Min Prices EURkWh JSON (P3 16:00-23:45)` | `Today JSON 15-Min Prices EURkWh (P3 16:00-23:45)` | | `ENTSO-E 15-Min Prices EURkWh 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 EURkWh (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` | `Today Highest Price Time` | | `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` | `Today Lowest 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` | `Today Highest Hourly 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` | `Today Lowest 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` | `Today Price Update Status` | | `Price Update Status` | `sensor.entso_e_prices_price_update_status` | `Today Price Update Status` | `sensor.eprices_today_price_update_status` |
| `Last Price Update Time` | `Today Data Loaded Time` | | `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` | `Today Price Update Status Message` | | `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` | `Today Current Price Status` | | `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 EURkWh JSON` | `Tomorrow JSON Hourly Prices EURkWh` | | `ENTSO-E Next Day Hourly Prices EURkWh JSON` | `sensor.entso_e_prices_entso_e_next_day_hourly_prices_eur_kwh_json` | `Tomorrow JSON Hourly Prices EURkWh` | `sensor.eprices_tomorrow_json_hourly_prices_eur_kwh` |
| `ENTSO-E Next Day 15-Min Prices EURkWh JSON (P1 00:00-07:45)` | `Tomorrow JSON 15-Min Prices EURkWh (P1 00:00-07:45)` | | `ENTSO-E Next Day 15-Min Prices EURkWh 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 EURkWh (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 EURkWh JSON (P2 08:00-15:45)` | `Tomorrow JSON 15-Min Prices EURkWh (P2 08:00-15:45)` | | `ENTSO-E Next Day 15-Min Prices EURkWh 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 EURkWh (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 EURkWh JSON (P3 16:00-23:45)` | `Tomorrow JSON 15-Min Prices EURkWh (P3 16:00-23:45)` | | `ENTSO-E Next Day 15-Min Prices EURkWh 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 EURkWh (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` | `Tomorrow Highest Price Time` | | `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` | `Tomorrow Lowest 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` | `Tomorrow Highest Hourly 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` | `Tomorrow Lowest 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` | `Tomorrow Price Update Status` | | `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` | `Tomorrow Data Loaded Time` | | `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` | `Tomorrow Price Update Status Message` | | `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` | `Tomorrow Current Price Status` | | `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` | `Last Reboot` | | `ENTSO-E Last Reboot` | `sensor.entso_e_prices_entso_e_last_reboot` | `Last Reboot` | `sensor.eprices_last_reboot` |
| `Entso-E Today NVS Status` | `Today NVS Status` | | `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` | `Tomorrow 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` | `Last Update Source` | | `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` | `Today Data Date` *(unchanged)* | | `Today Data Date` | `sensor.entso_e_prices_today_data_date` | `Today Data Date` *(unchanged)* | `sensor.eprices_today_data_date` |
| `Tomorrow Data Date` | `Tomorrow Data Date` *(unchanged)* | | `Tomorrow Data Date` | `sensor.entso_e_prices_tomorrow_data_date` | `Tomorrow Data Date` *(unchanged)* | `sensor.eprices_tomorrow_data_date` |
---
#### Buttons #### Buttons
| entso-e-prices v4.3.1 name | EPrices v1.0 name | | 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` | `Force Today's Update` | | `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` | `Force Tomorrow'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` | `Reboot Device` | | `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) ### New sensors in EPrices v1.0 (no equivalent in v4.3.1)
| Sensor | Purpose | | EPrices v1.2 name | EPrices v1.2 entity ID | Purpose |
|---|---| |---|---|---|
| `Today Last API Fetch Time` | Timestamp of last successful HTTP fetch for today | | `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` | Timestamp of last successful HTTP fetch for tomorrow | | `Tomorrow Last API Fetch Time` | `sensor.eprices_tomorrow_last_api_fetch_time` | Timestamp of last successful HTTP fetch for tomorrow |
| `WiFi Signal` | RSSI in dBm | | `WiFi Signal` | `sensor.eprices_wifi_signal` | RSSI in dBm |
| `Uptime` | Human-readable uptime string | | `Uptime` | `sensor.eprices_uptime` | Human-readable uptime string |
---
### Sensor output changes ### 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` | | `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` | | `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 Current Price Status` | `Valid` / `Missing` | `Valid` / `Missing` / `Stale` |
| `Today Data Loaded Time` | Stamped on HTTP fetch only | Stamped on NVS load and HTTP fetch | | `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 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 ### HA entity ID changes
All entity IDs changed due to the node name change from `entso-e-prices` to `eprices`. 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_ sensor.entso_e_prices_ → sensor.eprices_
button.entso_e_prices_ → button.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 ### NVS data
The NVS namespace changed from `entsoe2` to `eprices`. On first boot after 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. trigger a fresh HTTP fetch automatically. This is expected and safe.
No manual NVS erase is required. No manual NVS erase is required.
@@ -211,9 +236,10 @@ No manual NVS erase is required.
## Migration checklist ## Migration checklist
- [ ] Flash `eprices.yaml` to the device - [ ] Flash `eprices.yaml` to the device
- [ ] Update `secrets.yaml` — rename all `entsoe_` keys to `eprices_`; add `eprices_prov_fee` and `eprices_vat_rate` - [ ] 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: replace `entso_e_prices_` with `eprices_` in all entity IDs - [ ] In all HA automations: bulk replace `sensor.entso_e_prices_` `sensor.eprices_` and `button.entso_e_prices_``button.eprices_`
- [ ] Apply individual sensor name slug renames from the tables above - [ ] 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 - [ ] 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`) - [ ] 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
+15 -2
View File
@@ -22,7 +22,8 @@ Assistant automations are required for any core functionality.
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
- **Midnight bridge** — tomorrow's data automatically becomes today at 00:00, fully on-device - **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 - **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 - **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 - **Tomorrow live sensors** evaluate at `now + 86400s` — reflecting tomorrow at the same local time
@@ -133,6 +134,18 @@ 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) | | 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 | | 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 ### 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**
@@ -259,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` | | 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` | | 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 | | 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 ### Buttons
+64
View File
@@ -1,5 +1,69 @@
# EPrices Version History # 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 EURkWh` and `Tomorrow JSON Hourly Prices EURkWh`
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:5519: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 ## v1.1 — 2026-04-06
### Negative price provider fee ### Negative price provider fee
+71 -8
View File
@@ -1,10 +1,36 @@
# ============================================================================= # =============================================================================
# ESPHome: EPrices v1.1 # ESPHome: EPrices v1.2.1
# #
# 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: # 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:5519: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): # - Separate provider fee for negative spot prices (eprices_neg_prov_fee):
# positive prices: (raw / 1000) × (1 + prov_fee) × (1 + vat_rate) # positive prices: (raw / 1000) × (1 + prov_fee) × (1 + vat_rate)
# negative prices: (raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate) # negative prices: (raw / 1000) × (1 - neg_prov_fee) × (1 + vat_rate)
@@ -42,6 +68,7 @@
# eprices_country_bzn e.g. "SI" # eprices_country_bzn e.g. "SI"
# eprices_prov_fee e.g. "0.12" (provider fee, decimal multiplier) # eprices_prov_fee e.g. "0.12" (provider fee, decimal multiplier)
# eprices_vat_rate e.g. "0.22" (VAT rate, 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 README.md for full installation instructions and sensor reference.
# See CHANGELOG.md for complete sensor and entity ID listing. # See CHANGELOG.md for complete sensor and entity ID listing.
@@ -70,11 +97,19 @@ esphome:
} else { } else {
id(boot_time) = 0; 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: esp32:
board: esp32dev board: esp32dev
framework: framework:
type: esp-idf type: esp-idf
sdkconfig_options:
CONFIG_ESP_MAIN_TASK_STACK_SIZE: "16384"
# No include_builtin_idf_components needed — nvs headers are always # No include_builtin_idf_components needed — nvs headers are always
# available in esp-idf framework; eprices_nvs.h includes them directly. # available in esp-idf framework; eprices_nvs.h includes them directly.
@@ -185,6 +220,15 @@ time:
- seconds: /10 - seconds: /10
then: then:
- lambda: |- - 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)) { if (id(need_today_update) && !id(is_updating_today)) {
ESP_LOGI("eprices", "Worker: starting Today update"); ESP_LOGI("eprices", "Worker: starting Today update");
id(need_today_update) = false; id(need_today_update) = false;
@@ -194,6 +238,15 @@ time:
- seconds: /10 - seconds: /10
then: then:
- lambda: |- - 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(); auto t = id(ha_time).now();
bool in_window = (t.hour > 13 && t.hour < 23) || bool in_window = (t.hour > 13 && t.hour < 23) ||
(t.hour == 13 && t.minute >= 20) || (t.hour == 13 && t.minute >= 20) ||
@@ -322,7 +375,8 @@ time:
return; 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(); id(smart_tomorrow_price_update).execute();
# Attempts #3.. at 14:55, 15:55, 16:55, 17:55, 18:55, 19:55 (capped to 8) # Attempts #3.. at 14:55, 15:55, 16:55, 17:55, 18:55, 19:55 (capped to 8)
@@ -343,7 +397,8 @@ time:
return; 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(); id(smart_tomorrow_price_update).execute();
- minutes: /1 - minutes: /1
@@ -462,6 +517,9 @@ globals:
- id: is_updating_today - id: is_updating_today
type: bool type: bool
initial_value: 'false' initial_value: 'false'
- id: is_updating_today_since
type: time_t
initial_value: '0'
- id: tomorrow_last_successful_update - id: tomorrow_last_successful_update
type: time_t type: time_t
@@ -478,7 +536,7 @@ globals:
initial_value: 'false' initial_value: 'false'
- id: tomorrow_update_status_message - id: tomorrow_update_status_message
type: std::string type: std::string
initial_value: '"Waiting for 13:20"' initial_value: '"No data yet"'
- id: tomorrow_retry_count - id: tomorrow_retry_count
type: int type: int
initial_value: '0' initial_value: '0'
@@ -494,6 +552,9 @@ globals:
- id: is_updating_tomorrow - id: is_updating_tomorrow
type: bool type: bool
initial_value: 'false' initial_value: 'false'
- id: is_updating_tomorrow_since
type: time_t
initial_value: '0'
# --- Country code for Energy-Charts API (wired from secret) --- # --- Country code for Energy-Charts API (wired from secret) ---
- id: country_bzn - id: country_bzn
@@ -1769,6 +1830,7 @@ script:
return; return;
} }
id(is_updating_today) = true; 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(last_update_attempt) = id(ha_time).now().timestamp;
id(retry_count)++; id(retry_count)++;
{ char buf[8]; snprintf(buf, sizeof(buf), "%d", id(retry_count)); id(today_price_update_attempts).publish_state(buf); } { char buf[8]; snprintf(buf, sizeof(buf), "%d", id(retry_count)); id(today_price_update_attempts).publish_state(buf); }
@@ -1872,6 +1934,7 @@ script:
return; return;
} }
id(is_updating_tomorrow) = true; 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_last_update_attempt) = id(ha_time).now().timestamp;
id(tomorrow_retry_count)++; id(tomorrow_retry_count)++;
{ char buf[8]; snprintf(buf, sizeof(buf), "%d", id(tomorrow_retry_count)); id(tomorrow_price_update_attempts).publish_state(buf); } { char buf[8]; snprintf(buf, sizeof(buf), "%d", id(tomorrow_retry_count)); id(tomorrow_price_update_attempts).publish_state(buf); }
@@ -2059,7 +2122,7 @@ script:
id(today_current_max_hourly_price_percentage).publish_state(NAN); id(today_current_max_hourly_price_percentage).publish_state(NAN);
id(last_update_success) = false; id(last_update_success) = false;
id(update_status_message) = "Cleared awaiting NVS or HTTP"; 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_p1_00_00_07_45).publish_state("");
id(json_15min_prices_kwh_p2_08_00_15_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(""); id(json_15min_prices_kwh_p3_16_00_23_45).publish_state("");
@@ -2092,7 +2155,7 @@ script:
id(tomorrow_min_hourly_price).publish_state(NAN); id(tomorrow_min_hourly_price).publish_state(NAN);
id(tomorrow_max_hourly_price).publish_state(NAN); id(tomorrow_max_hourly_price).publish_state(NAN);
id(tomorrow_current_max_hourly_price_percentage).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_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_p2_08_00_15_45).publish_state("");
id(json_tomorrow_15min_prices_kwh_p3_16_00_23_45).publish_state(""); id(json_tomorrow_15min_prices_kwh_p3_16_00_23_45).publish_state("");
@@ -2101,7 +2164,7 @@ script:
id(tomorrow_min_hourly_price_time_str) = "--:--"; id(tomorrow_min_hourly_price_time_str) = "--:--";
id(tomorrow_max_hourly_price_time_str) = "--:--"; id(tomorrow_max_hourly_price_time_str) = "--:--";
id(tomorrow_last_update_success) = false; 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_current_price_status_str) = "Cleared";
id(tomorrow_price_update_status).update(); id(tomorrow_price_update_status).update();
id(tomorrow_price_status_message).update(); id(tomorrow_price_status_message).update();