fix(strommix): per-field fallback to Energy-Charts when SMARD value missing
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:
@@ -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 & 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 & 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: {fmtTime(live.measuredAtUnix)} Uhr.
|
||||
Konventionell = Braunkohle + Steinkohle + Erdgas + Kernkraft (Pumpspeicher,
|
||||
Sonstige Konventionelle und Wasserkraft sind separat ausgewiesen).
|
||||
|
||||
+8
-4
@@ -116,8 +116,12 @@ export type SmardSnapshot = {
|
||||
* set, so every value is from a slot that all filters have reported.
|
||||
* Unix seconds (not ms). */
|
||||
measuredAtUnix: number;
|
||||
/** Average MW for each filter at the aligned slot. Missing filters → 0. */
|
||||
values: Record<SmardFilter, number>;
|
||||
/** Average MW for each filter at the aligned slot. `null` means the
|
||||
* filter could not be resolved (404, all-null series, fetch error)
|
||||
* — distinguished from a genuine zero so callers can choose a
|
||||
* fallback source per-field instead of ignoring real zeroes
|
||||
* (e.g. solar at night, nuclear in DE). */
|
||||
values: Record<SmardFilter, number | null>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -174,9 +178,9 @@ export async function fetchSmardSnapshot(
|
||||
})
|
||||
);
|
||||
|
||||
const values = {} as Record<SmardFilter, number>;
|
||||
const values = {} as Record<SmardFilter, number | null>;
|
||||
for (const key of Object.keys(SMARD_FILTERS) as SmardFilter[]) {
|
||||
values[key] = 0;
|
||||
values[key] = null;
|
||||
}
|
||||
for (const [key, mwh] of aligned) {
|
||||
if (mwh != null) {
|
||||
|
||||
+7
-4
@@ -11,10 +11,13 @@ export type StrommixResponse = {
|
||||
// the rest of the values. Cross-border + prices may have their own,
|
||||
// earlier or later, timestamps.
|
||||
measuredAtUnix: number | null;
|
||||
/** Where production + load came from. `smard` is canonical (German
|
||||
* grid feed); `energy-charts` is the fallback when SMARD is
|
||||
* unreachable. */
|
||||
dataSource: "smard" | "energy-charts";
|
||||
/** Where production + load came from. `smard` = pure SMARD (canonical);
|
||||
* `smard+energy-charts` = SMARD with Energy-Charts as per-field
|
||||
* fallback for filters SMARD couldn't resolve at the snapshot's slot
|
||||
* (typically the load filter when SMARD's pipeline lags more than
|
||||
* the production filters); `energy-charts` = SMARD entirely
|
||||
* unreachable, full fallback. */
|
||||
dataSource: "smard" | "smard+energy-charts" | "energy-charts";
|
||||
// Production (MW)
|
||||
windOnshoreMw: number;
|
||||
windOffshoreMw: number;
|
||||
|
||||
Reference in New Issue
Block a user