feat(strommix): history-driven panels (residual load, dunkelflaute, neg-price, flow balance)
Aggregator route /api/strommix now reads three RustyCMS history-mode collections in parallel — strommix_history_de, price_de_history, crossborder_history_de — and projects derived metrics on top of the raw upstream snapshots stored there: - todayHourly: per-slot Last + Wind+Solar for the residual-load chart - negPriceHoursYtd: count of YTD slots where DE-LU day-ahead < 0 - crossborderTodayGwh / netCrossborderGwhYtd / crossborderByCountryGwhYtd: GW × slot-hours integrated into a per-country GWh balance - lastDunkelflaute / worstDunkelflauten: contiguous runs where (wind+solar)/load drops below 10 % for at least 1 h, top 5 by duration All four are null-graceful — collections that the CMS hasn't backfilled yet leave the corresponding panel hidden instead of breaking the widget. Five new components in lib/components/blocks/strommix/: - ResidualLoadChart.svelte — SVG line chart, no chart-lib dep - CapacityFactorBars.svelte — live vs installed Wind/Wind-Off/Solar - DunkelflauteCounter.svelte — last phase card + top-5 list - NegPriceCounter.svelte — YTD hours + manual EinsMan compensation - FlowBalanceTodayYtd.svelte — today + YTD saldo with per-country breakdown Block schema additions (block-types.ts + strommix-default.json5 in cms_content): installed_*_gw / installed_as_of for the capacity-factor bars (BNetzA Marktstammdatenregister snapshot, manually maintained), and einsman_costs_ytd_eur_mio / einsman_costs_as_of for the NegPrice panel (BNetzA quarterly report figures, no API).
This commit is contained in:
@@ -50,4 +50,62 @@ export type StrommixResponse = {
|
||||
staleStrommix: boolean;
|
||||
staleCrossborder: boolean;
|
||||
stalePrice: boolean;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Historical aggregates (sourced from CMS `_history` endpoint over
|
||||
// raw upstream snapshots; null = no data yet for this widget).
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/** Today's residual-load curve. One entry per upstream slot (typically
|
||||
* 15 min). `loadMw - weatherRenewableMw` = the gap conventional plants
|
||||
* + imports must fill. `null` when history backend is empty / disabled. */
|
||||
todayHourly: ResidualLoadPoint[] | null;
|
||||
|
||||
/** Negative-price hours year-to-date (DE-LU, day-ahead). Counted by
|
||||
* iterating history snapshots and summing 15-min slots whose price
|
||||
* was below 0. */
|
||||
negPriceHoursYtd: number | null;
|
||||
|
||||
/** Cross-border net balance YTD in GWh (positive = net export from DE,
|
||||
* negative = net import). */
|
||||
netCrossborderGwhYtd: number | null;
|
||||
|
||||
/** Per-country YTD balance, same sign convention as `netFlowGw`. */
|
||||
crossborderByCountryGwhYtd: Record<string, number> | null;
|
||||
|
||||
/** Cross-border balance for *today only*, GWh per country and total.
|
||||
* Same sign convention. `null` when crossborder history is empty. */
|
||||
crossborderTodayGwh: { total: number; perCountry: Record<string, number> } | null;
|
||||
|
||||
/** Most recent contiguous run of `(wind+solar) / load < 10%`. `null`
|
||||
* when no such run exists in retained history. `endedHoursAgo: 0`
|
||||
* means the run is still ongoing (last slot is within the threshold). */
|
||||
lastDunkelflaute: DunkelflautePhase | null;
|
||||
|
||||
/** Top 5 longest Dunkelflaute phases in retained history, newest first
|
||||
* if equal length. Empty array when history exists but no qualifying
|
||||
* phase found. `null` when history is unavailable. */
|
||||
worstDunkelflauten: DunkelflautePhase[] | null;
|
||||
};
|
||||
|
||||
/** One upstream slot in the residual-load series. */
|
||||
export type ResidualLoadPoint = {
|
||||
/** Unix seconds at the start of the slot. */
|
||||
unix: number;
|
||||
/** Total electrical load on the German grid (MW). */
|
||||
loadMw: number;
|
||||
/** Wind onshore + wind offshore + solar (MW). */
|
||||
weatherRenewableMw: number;
|
||||
};
|
||||
|
||||
/** A contiguous low-renewables run, computed from history snapshots. */
|
||||
export type DunkelflautePhase = {
|
||||
/** Unix sec of the first slot below the threshold. */
|
||||
startUnix: number;
|
||||
/** Run duration in hours (slot count × slot length). */
|
||||
durationH: number;
|
||||
/** Lowest Wind+Solar share observed during the run, percentage of load. */
|
||||
minPct: number;
|
||||
/** Hours since the run ended (0 = ongoing). */
|
||||
endedHoursAgo: number;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user