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
+8 -4
View File
@@ -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) {