fix(home-assistant): EPEX/Spotty consumer price broken by epex_spot 4.x rename + add future-price chart #217

Closed
opened 2026-07-05 17:01:17 +02:00 by dominik.polakovics · 1 comment

Summary

Home Assistant (host fw, hass container) shows a constant, wrong consumer electricity price. Root cause: the epex_spot custom component was updated to 4.x, which renamed its sensors (pricemarket_price, net_pricetotal_price) and switched units (ct/kWh → €/kWh). The template in hosts/fw/modules/home-assistant/electricity.nix still references the now-orphaned sensor.epex_spot_data_price (permanently unavailable):

states('sensor.epex_spot_data_price') -> 'unavailable' -> | float -> 0
electricity_price = 0 + 0.1039 = constant 0.1039 €/kWh

Two defects, really: the dead entity reference and the original formula never applied 20% VAT (so it read ~20% low even when it worked). Note the epex_spot component itself loads fine — the aggregate sensors (average/median/lowest/highest/rank/quantile) work; only the current-interval price/net_price entities are orphaned.

Rebuild the price sensor declaratively in Nix (design option C) sourced from sensor.epex_spot_data_market_price, applying the full Spotty + grid + tax fee model incl. VAT, and exposing a forecast attribute so a future-price ApexCharts chart can render (option X: card installed in Nix, chart placed via the UI).

Fee model (from the June 2026 Spotty invoice — reconciles to the cent)

Marginal consumer price = (market_price + fees_net) × 1.20, with fees_net in net €/kWh:

Component Net rate (€/kWh)
Spotty Service Fee (Aufschlag) 0.0149
Netznutzung NE7 (blend: Einfach 0.0698 + SNAP 0.0558) ~0.0620
Netzverlust NE7 0.0070
Gebrauchsabgabe (municipal ~7% of grid, Klagenfurt) ~0.0099
EAG Förderbeitrag (Netznutzung + Netzverlust) 0.0062
Elektrizitätsabgabe (2026 temporary rate; reverts to 0.015 in 2027) 0.0010
Σ fees_net ~0.1010
USt / VAT × 1.20

Model as named let constants, one per line, each commented with its invoice line + effective date (so the 2027 Abgabe revert is a one-line edit). Fixed monthly costs (Grundgebühr, Messpreis, Netzleistung, Pauschalen) are excluded — this sensor is marginal €/kWh, which is exactly what a "when is it cheap" chart wants.

Historical anchor: the old hardcoded 0.1039 was ~ the correct net offset (0.1010) — the original bug was forgetting × 1.20.

Plan

  1. electricity.nix: replace the legacy platform: template sensor with a modern template: / sensor: sensor (also removes the HA 2026.6 legacy-template-entity breakage the default.nix pin is deliberately holding below). Keep the entity id sensor.electricity_price so nothing downstream breaks.
    • state = (market_price + fees_net) × 1.20.
    • forecast attribute = market_price's data list mapped to [{start, price: (market + fees_net) × 1.20}, ...]. Exclude this attribute from the recorder (DB bloat: up to ~192 entries at 15-min × 2 days).
  2. Install the card: add pkgs.home-assistant-custom-lovelace-modules.apexcharts-card to services.home-assistant.customLovelaceModules (nixpkgs ships it; auto-registers as a Lovelace resource). Pattern mirrors ac.nix + custom-components/lovelace-scheduler.nix.
  3. Chart: ApexCharts card YAML — bars over sensor.electricity_price's forecast, today + tomorrow (tomorrow appears after ~14:00), cheap→expensive coloring, "now" marker, optional raw-spot overlay to show the tax wedge. Placed on a dashboard via the UI (dashboards are storage-mode). Capture the YAML in the PR for reproducibility.
  4. Deploy: branch from origin/main -> pre-commit dry-builds fw -> tea pr create -> merge -> bento pulls (~5 min) and HA restarts.

Acceptance criteria

  • sensor.electricity_price tracks live (market_price + fees) × 1.20, updates each interval, no longer constant.
  • Built with modern template: syntax (not legacy platform: template).
  • Fees are named, commented let constants matching the invoice; 20% VAT applied.
  • sensor.electricity_price exposes a forecast attribute: {start, price} consumer prices for all known future intervals (today + tomorrow), excluded from the recorder.
  • apexcharts-card installed declaratively via customLovelaceModules.
  • A future-price chart renders in the UI from the forecast attribute; card YAML recorded in the PR.
  • Orphaned sensor.epex_spot_data_price / sensor.epex_spot_data_net_price entities deleted (cosmetic).
  • fw pre-commit dry-build green; verified on the running instance.

Open question (acceptance-blocker for the forecast mapping)

The exact localized price-key inside sensor.epex_spot_data_market_price's data entries — it is self._localized.attr_name_per_kwh in the 4.x source, not literally price. Read it (and confirm hourly vs 15-min resolution) from Developer Tools -> States. The forecast Jinja mapping can't be finalized until this key is known. Once provided, this issue is ready-for-agent.

References

  • Broken file: hosts/fw/modules/home-assistant/electricity.nix
  • Card-install pattern: hosts/fw/modules/home-assistant/ac.nix (customLovelaceModules)
  • Upstream rename: mampfes/ha_epex_spot 4.0.0 (price -> market_price, net_price -> total_price, ct/kWh -> €/kWh)

Blocked by

None — can start immediately (modulo the forecast-key lookup above).

## Summary Home Assistant (host `fw`, `hass` container) shows a **constant, wrong** consumer electricity price. Root cause: the `epex_spot` custom component was updated to **4.x**, which **renamed** its sensors (`price` → `market_price`, `net_price` → `total_price`) and switched units (ct/kWh → €/kWh). The template in `hosts/fw/modules/home-assistant/electricity.nix` still references the now-orphaned `sensor.epex_spot_data_price` (permanently `unavailable`): ``` states('sensor.epex_spot_data_price') -> 'unavailable' -> | float -> 0 electricity_price = 0 + 0.1039 = constant 0.1039 €/kWh ``` Two defects, really: the dead entity reference **and** the original formula never applied 20% VAT (so it read ~20% low even when it worked). Note the `epex_spot` component itself loads fine — the aggregate sensors (average/median/lowest/highest/rank/quantile) work; only the current-interval `price`/`net_price` entities are orphaned. Rebuild the price sensor **declaratively in Nix** (design option C) sourced from `sensor.epex_spot_data_market_price`, applying the full Spotty + grid + tax fee model incl. VAT, and exposing a **`forecast` attribute** so a future-price **ApexCharts** chart can render (option X: card installed in Nix, chart placed via the UI). ## Fee model (from the June 2026 Spotty invoice — reconciles to the cent) Marginal consumer price = `(market_price + fees_net) × 1.20`, with `fees_net` in net €/kWh: | Component | Net rate (€/kWh) | |---|---| | Spotty Service Fee (Aufschlag) | 0.0149 | | Netznutzung NE7 (blend: Einfach 0.0698 + SNAP 0.0558) | ~0.0620 | | Netzverlust NE7 | 0.0070 | | Gebrauchsabgabe (municipal ~7% of grid, Klagenfurt) | ~0.0099 | | EAG Förderbeitrag (Netznutzung + Netzverlust) | 0.0062 | | Elektrizitätsabgabe (2026 temporary rate; reverts to 0.015 in 2027) | 0.0010 | | **Σ fees_net** | **~0.1010** | | USt / VAT | × 1.20 | Model as **named `let` constants**, one per line, each commented with its invoice line + effective date (so the 2027 Abgabe revert is a one-line edit). Fixed monthly costs (Grundgebühr, Messpreis, Netzleistung, Pauschalen) are **excluded** — this sensor is marginal €/kWh, which is exactly what a "when is it cheap" chart wants. Historical anchor: the old hardcoded `0.1039` was ~ the correct *net* offset (0.1010) — the original bug was forgetting `× 1.20`. ## Plan 1. `electricity.nix`: replace the legacy `platform: template` sensor with a **modern** `template:` / `sensor:` sensor (also removes the HA 2026.6 legacy-template-entity breakage the `default.nix` pin is deliberately holding below). Keep the entity id `sensor.electricity_price` so nothing downstream breaks. - `state` = `(market_price + fees_net) × 1.20`. - `forecast` attribute = `market_price`'s `data` list mapped to `[{start, price: (market + fees_net) × 1.20}, ...]`. Exclude this attribute from the recorder (DB bloat: up to ~192 entries at 15-min × 2 days). 2. Install the card: add `pkgs.home-assistant-custom-lovelace-modules.apexcharts-card` to `services.home-assistant.customLovelaceModules` (nixpkgs ships it; auto-registers as a Lovelace resource). Pattern mirrors `ac.nix` + `custom-components/lovelace-scheduler.nix`. 3. Chart: ApexCharts card YAML — bars over `sensor.electricity_price`'s `forecast`, today + tomorrow (tomorrow appears after ~14:00), cheap→expensive coloring, "now" marker, optional raw-spot overlay to show the tax wedge. Placed on a dashboard via the UI (dashboards are storage-mode). Capture the YAML in the PR for reproducibility. 4. Deploy: branch from `origin/main` -> pre-commit dry-builds `fw` -> `tea pr create` -> merge -> bento pulls (~5 min) and HA restarts. ## Acceptance criteria - [ ] `sensor.electricity_price` tracks live `(market_price + fees) × 1.20`, updates each interval, no longer constant. - [ ] Built with modern `template:` syntax (not legacy `platform: template`). - [ ] Fees are named, commented `let` constants matching the invoice; 20% VAT applied. - [ ] `sensor.electricity_price` exposes a `forecast` attribute: `{start, price}` consumer prices for all known future intervals (today + tomorrow), excluded from the recorder. - [ ] `apexcharts-card` installed declaratively via `customLovelaceModules`. - [ ] A future-price chart renders in the UI from the `forecast` attribute; card YAML recorded in the PR. - [ ] Orphaned `sensor.epex_spot_data_price` / `sensor.epex_spot_data_net_price` entities deleted (cosmetic). - [ ] `fw` pre-commit dry-build green; verified on the running instance. ## Open question (acceptance-blocker for the forecast mapping) The exact **localized price-key** inside `sensor.epex_spot_data_market_price`'s `data` entries — it is `self._localized.attr_name_per_kwh` in the 4.x source, **not** literally `price`. Read it (and confirm hourly vs 15-min resolution) from Developer Tools -> States. The `forecast` Jinja mapping can't be finalized until this key is known. Once provided, this issue is `ready-for-agent`. ## References - Broken file: `hosts/fw/modules/home-assistant/electricity.nix` - Card-install pattern: `hosts/fw/modules/home-assistant/ac.nix` (`customLovelaceModules`) - Upstream rename: mampfes/ha_epex_spot 4.0.0 (`price` -> `market_price`, `net_price` -> `total_price`, ct/kWh -> €/kWh) ## Blocked by None — can start immediately (modulo the forecast-key lookup above).
Author
Owner

This was generated by AI during triage.

Agent Brief

Category: bug
Summary: Rebuild sensor.electricity_price on the epex_spot 4.x entities with the full Spotty fee model incl. 20% VAT, expose a consumer-price forecast attribute, and install apexcharts-card declaratively.

Current behavior (reproduced live during triage):
The fw Home Assistant instance (hass container) defines sensor.electricity_price as a legacy platform: template sensor reading sensor.epex_spot_data_price — an entity orphaned by the epex_spot 4.x rename. Deployed component version is 4.1.0 (nixpkgs 26.05); HA itself is 2026.5.4 via the dedicated pin. The container journal shows a recurring error on every render:

TemplateError: ValueError: Template error: float got invalid input 'unavailable' ...
for attribute '_attr_native_value' in entity 'sensor.electricity_price'

Note this corrects the issue body: on HA 2026.5, | float without a default raises, so the entity goes stale/unavailable rather than reading a constant 0.1039. Additionally the old formula never applied 20% VAT. The epex_spot integration itself loads fine; its aggregate sensors work.

Resolved during triage (was the open question):
Read from the deployed 4.1.0 source in fw's nix store:

  • The localized per-kWh key in sensor.epex_spot_data_market_price's data attribute is the literal string price_per_kwh (EUR mapping → ATTR_PRICE_PER_KWH).
  • Each data entry is {"start_time": <local ISO8601>, "end_time": <local ISO8601>, "price_per_kwh": <float, €/kWh>}. The sensor also exposes a top-level price_per_kwh equal to its state.
  • Interval resolution (hourly vs 15-min) could not be read from outside (config entry lives in DAC-protected .storage). Non-blocking: write the forecast mapping and chart data_generator to iterate entries generically using start_time, so either resolution works. Confirm actual resolution after deploy.

Desired behavior:

  • sensor.electricity_price (keep this entity id — downstream consumers depend on it) is a modern template:-style sensor: state = (sensor.epex_spot_data_market_price + fees_net) × 1.20, unit €/kWh, updating each market interval.
  • fees_net ≈ 0.1010 €/kWh, built from named let constants in Nix, one per invoice component, each commented with its invoice line + effective date (see fee table in the issue body; the Elektrizitätsabgabe 0.0010 reverts to 0.015 in 2027 — must be a one-line edit).
  • The sensor exposes a forecast attribute: the data list mapped to [{start, price}] where price = (price_per_kwh + fees_net) × 1.20, covering all known future intervals. This attribute is excluded from the recorder (DB bloat).
  • apexcharts-card is installed declaratively via services.home-assistant.customLovelaceModules (nixpkgs attr home-assistant-custom-lovelace-modules.apexcharts-card, verified present in 26.05 at v2.2.3). Pattern: same as the existing lovelace-scheduler module install.
  • ApexCharts card YAML (bars over the forecast attribute, today + tomorrow, now-marker) is authored and captured in the PR description for the human to paste into the storage-mode dashboard.

Key interfaces:

  • services.home-assistant.config in the fw hass container config — replace the legacy sensor = [{platform = "template"; ...}] block with modern template syntax; this also removes one blocker for the HA 2026.6 pin bump tracked separately.
  • Entity ids: sensor.epex_spot_data_market_price (source), sensor.electricity_price (must survive unchanged).
  • Recorder exclusion: recorder.exclude config or template attribute exclusion — whichever HA 2026.5 supports for attributes.
  • Verification channel: diag SSH (ADR-0005) — container journal readable via journalctl -D /var/lib/nixos-containers/hass/var/log/journal on fw.

Acceptance criteria:

  • sensor.electricity_price state = (market_price + fees_net) × 1.20, updates each interval; the recurring TemplateError for this entity no longer appears in the hass container journal after HA restarts post-deploy.
  • Modern template: syntax; no legacy platform: template sensor remains in the electricity module.
  • Fees are named, commented let constants matching the invoice table; 20% VAT applied as a final multiplier.
  • forecast attribute exposes {start, price} consumer prices for all future intervals present in the source data attribute, and is excluded from the recorder.
  • apexcharts-card installed via customLovelaceModules; card YAML included in the PR.
  • fw pre-commit dry-build green; PR opened (not pushed to main).

Out of scope (human follow-ups after merge):

  • Placing the chart on a dashboard (storage-mode, UI action) and visually confirming it renders.
  • Deleting the orphaned sensor.epex_spot_data_price / sensor.epex_spot_data_net_price registry entries (UI cosmetic cleanup).
  • Fixed monthly costs (Grundgebühr, Messpreis, etc.) — this sensor is marginal €/kWh only.
  • The broader legacy-template migration + HA 2026.6 pin bump (tracked in #137).
> *This was generated by AI during triage.* ## Agent Brief **Category:** bug **Summary:** Rebuild `sensor.electricity_price` on the epex_spot 4.x entities with the full Spotty fee model incl. 20% VAT, expose a consumer-price `forecast` attribute, and install apexcharts-card declaratively. **Current behavior (reproduced live during triage):** The fw Home Assistant instance (hass container) defines `sensor.electricity_price` as a *legacy* `platform: template` sensor reading `sensor.epex_spot_data_price` — an entity orphaned by the epex_spot 4.x rename. Deployed component version is **4.1.0** (nixpkgs 26.05); HA itself is 2026.5.4 via the dedicated pin. The container journal shows a recurring error on every render: ``` TemplateError: ValueError: Template error: float got invalid input 'unavailable' ... for attribute '_attr_native_value' in entity 'sensor.electricity_price' ``` Note this corrects the issue body: on HA 2026.5, `| float` without a default **raises**, so the entity goes stale/unavailable rather than reading a constant 0.1039. Additionally the old formula never applied 20% VAT. The epex_spot integration itself loads fine; its aggregate sensors work. **Resolved during triage (was the open question):** Read from the *deployed* 4.1.0 source in fw's nix store: - The localized per-kWh key in `sensor.epex_spot_data_market_price`'s `data` attribute is the literal string **`price_per_kwh`** (EUR mapping → `ATTR_PRICE_PER_KWH`). - Each `data` entry is `{"start_time": <local ISO8601>, "end_time": <local ISO8601>, "price_per_kwh": <float, €/kWh>}`. The sensor also exposes a top-level `price_per_kwh` equal to its state. - Interval resolution (hourly vs 15-min) could not be read from outside (config entry lives in DAC-protected `.storage`). **Non-blocking:** write the forecast mapping and chart `data_generator` to iterate entries generically using `start_time`, so either resolution works. Confirm actual resolution after deploy. **Desired behavior:** - `sensor.electricity_price` (keep this entity id — downstream consumers depend on it) is a **modern** `template:`-style sensor: state = (`sensor.epex_spot_data_market_price` + fees_net) × 1.20, unit €/kWh, updating each market interval. - fees_net ≈ 0.1010 €/kWh, built from **named let constants in Nix**, one per invoice component, each commented with its invoice line + effective date (see fee table in the issue body; the Elektrizitätsabgabe 0.0010 reverts to 0.015 in 2027 — must be a one-line edit). - The sensor exposes a `forecast` attribute: the `data` list mapped to `[{start, price}]` where price = (`price_per_kwh` + fees_net) × 1.20, covering all known future intervals. This attribute is **excluded from the recorder** (DB bloat). - `apexcharts-card` is installed declaratively via `services.home-assistant.customLovelaceModules` (nixpkgs attr `home-assistant-custom-lovelace-modules.apexcharts-card`, verified present in 26.05 at v2.2.3). Pattern: same as the existing lovelace-scheduler module install. - ApexCharts card YAML (bars over the `forecast` attribute, today + tomorrow, now-marker) is authored and captured in the PR description for the human to paste into the storage-mode dashboard. **Key interfaces:** - `services.home-assistant.config` in the fw hass container config — replace the legacy `sensor = [{platform = "template"; ...}]` block with modern `template` syntax; this also removes one blocker for the HA 2026.6 pin bump tracked separately. - Entity ids: `sensor.epex_spot_data_market_price` (source), `sensor.electricity_price` (must survive unchanged). - Recorder exclusion: `recorder.exclude` config or template attribute exclusion — whichever HA 2026.5 supports for attributes. - Verification channel: diag SSH (ADR-0005) — container journal readable via `journalctl -D /var/lib/nixos-containers/hass/var/log/journal` on fw. **Acceptance criteria:** - [ ] `sensor.electricity_price` state = (market_price + fees_net) × 1.20, updates each interval; the recurring `TemplateError` for this entity no longer appears in the hass container journal after HA restarts post-deploy. - [ ] Modern `template:` syntax; no legacy `platform: template` sensor remains in the electricity module. - [ ] Fees are named, commented let constants matching the invoice table; 20% VAT applied as a final multiplier. - [ ] `forecast` attribute exposes `{start, price}` consumer prices for all future intervals present in the source `data` attribute, and is excluded from the recorder. - [ ] apexcharts-card installed via `customLovelaceModules`; card YAML included in the PR. - [ ] fw pre-commit dry-build green; PR opened (not pushed to main). **Out of scope (human follow-ups after merge):** - Placing the chart on a dashboard (storage-mode, UI action) and visually confirming it renders. - Deleting the orphaned `sensor.epex_spot_data_price` / `sensor.epex_spot_data_net_price` registry entries (UI cosmetic cleanup). - Fixed monthly costs (Grundgebühr, Messpreis, etc.) — this sensor is marginal €/kWh only. - The broader legacy-template migration + HA 2026.6 pin bump (tracked in #137).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/nixos#217
No description provided.