fix(strommix): per-field fallback to Energy-Charts when SMARD value missing
Deploy / verify (push) Successful in 46s
Deploy / deploy (push) Successful in 48s

The widget rendered "Keine gültigen Live-Werte" the moment SMARD's
load filter (id 410) lagged behind the production filters by even one
slot — the aggregator coalesced "filter unavailable" with "filter
returned 0" and the percent calculation refused to divide by zero.

Three changes:

1. SmardSnapshot.values now uses `number | null` so callers can tell
   "filter genuinely missing" apart from "filter reported zero" (e.g.
   solar at night, decommissioned nuclear).
2. Aggregator pulls Energy-Charts (CMS strommix_de) in parallel and
   uses it as a per-field fallback for any SMARD null. New
   `dataSource: "smard+energy-charts"` flag travels in the response so
   the attribution stays honest. Pure SMARD when everything resolves;
   pure Energy-Charts when SMARD is entirely down; the hybrid label
   for the common case where only load drops out.
3. Widget renders the production breakdown + context row + price row
   even when the percentage is unavailable, with a clear "Lastwert
   von SMARD gerade nicht verfügbar" hint instead of going blank.
This commit is contained in:
Peter Meier
2026-05-04 23:55:57 +02:00
parent 96cd9e9c57
commit 2d85681252
4 changed files with 95 additions and 35 deletions
+25 -13
View File
@@ -186,18 +186,25 @@
</div>
{:else if !live}
<div class="p-6 text-center text-neutral-500 text-sm">Lade Live-Daten…</div>
{:else if renewablePct == null}
<div class="p-6 text-sm text-amber-700">Keine gültigen Live-Werte verfügbar.</div>
{:else}
<div class="p-6 text-center">
<div class="text-6xl sm:text-7xl font-extrabold tabular-nums {accentClasses.pct}">
{Math.round(renewablePct)}%
</div>
<div class="mt-1 text-sm text-neutral-700">Wind &amp; Solar am Bedarf</div>
<div class="mt-3 text-base font-semibold {accentClasses.label}">{statusLabel}</div>
{#if argumentHtml}
<div class="prose prose-sm mt-3 max-w-prose mx-auto text-neutral-800">
{@html argumentHtml}
{#if renewablePct != null}
<div class="text-6xl sm:text-7xl font-extrabold tabular-nums {accentClasses.pct}">
{Math.round(renewablePct)}%
</div>
<div class="mt-1 text-sm text-neutral-700">Wind &amp; Solar am Bedarf</div>
<div class="mt-3 text-base font-semibold {accentClasses.label}">{statusLabel}</div>
{#if argumentHtml}
<div class="prose prose-sm mt-3 max-w-prose mx-auto text-neutral-800">
{@html argumentHtml}
</div>
{/if}
{:else}
<!-- Production data arrived but load is missing → no percentage,
but we still show what we have so the widget isn't blank. -->
<div class="text-sm text-amber-700">
Lastwert von SMARD gerade nicht verfügbar — Prozentanzeige ausgesetzt.
Erzeugung wird unten in der Detailansicht weiter angezeigt.
</div>
{/if}
</div>
@@ -270,9 +277,14 @@
<div class="text-neutral-500 font-semibold">Last</div><div class="font-semibold">{fmtMw(live.loadMw)}</div>
</div>
<p class="mt-1 text-[11px] text-neutral-500">
Quelle: {live.dataSource === "smard"
? "smard.de (Bundesnetzagentur)"
: "energy-charts.info (ENTSO-E + SMARD)"}
Quelle:
{#if live.dataSource === "smard"}
smard.de (Bundesnetzagentur)
{:else if live.dataSource === "smard+energy-charts"}
smard.de + energy-charts.info (Last-Wert ergänzt)
{:else}
energy-charts.info (ENTSO-E + SMARD)
{/if}
— 15-Min-Auflösung, Stand:&nbsp;{fmtTime(live.measuredAtUnix)}&nbsp;Uhr.
Konventionell = Braunkohle + Steinkohle + Erdgas + Kernkraft (Pumpspeicher,
Sonstige Konventionelle und Wasserkraft sind separat ausgewiesen).