Frage im Forum

Hours Demand — „Wie viele Ladestunden braucht meine Batterie — und wann?“

POST /v1/decision/hours-demand — EMS-Stundenplan aus SOC, Last und PV. Loxone-Hauptpfad: Hours & Telemetrie mit Extract v1.23. Offene API für HA/Python; Community 200 Anfragen/Tag.

Was macht diese Funktion? (ohne Fachchinesisch)

Stellen Sie sich vor, Ihre Batterie ist zu 35 % voll. Das Haus verbraucht heute noch Strom. Die PV-Anlage liefert etwas. Der Spotpreis schwankt.

Hours Demand beantwortet:

1. Wie viele der günstigsten Netz-Ladestunden Sie heute freigeben sollten (hours_needed_today) — der volle Plan steht in hours_needed / day_plan 2. Welche Uhrzeiten das sind (charge_hours_today bzw. charge_hours) — inkl. Preis in ct/kWh

Alltagsbeispiel: „Heute brauchen Sie 3 Ladestunden. Am sinnvollsten: 13:00, 14:00 und 03:00 Uhr. Über die Woche plant das System weitere günstige Stunden — inkl. Vorrat (P20 + Absolutschutz, Cap unter Ziel-SoC), wenn ein Folgetag eine Lücke hat und der Speicher die kWh noch tragen kann; Dump-Sizing vor weiterem Vorrat, Placement Near-Cover → Soft-Cap-Abandon-Spill (Flag/Darf-S1) → Prep → Far-Cover — sichtbar unter Anlagenprognose.“

Ihr EMS (z. B. Loxone Spot-Preis-Optimierer) bekommt die heutige Zahl — und optional die genauen Startzeiten.

Wann nutzen?

  • Sie haben einen Speicher (SOC in %)
  • Sie wollen die Rechnung nicht selbst bauen

Nicht nötig, wenn Sie schon wissen „immer genau 4 Stunden“ → Windows.

Zwei Modi

ModusWas passiertVergleich
decide (Standard)Bilanz rechnen + Ladeplan liefernwie ein fertiger Vorschlag
telemetryNur SOC + Zähler speichernStatus-Update; intern Decide nahe Min-SoC (ab + auf)

Für häufige Messwerte besser der eigene Endpoint Plant Telemetry (gleiche Idee, eigenes Kontingent).

Endpoint


POST https://api.spotpriceapi.com/v1/decision/hours-demand

Header:


X-API-Key: sf_live_YOUR_KEY
Content-Type: application/json

Mindestens Community-Zugang.

Was Sie vorher im Portal eintragen sollten

Unter Standort / Anlagenprofil idealerweise:

  • Speicherkapazität (kWh)
  • Netzanschlussleistung (kW) — begrenzt die Netzladeleistung zusammen mit Profil-Max. Netzladung (Default 8 kW): O11 / charge_power_kw = min(WR, Anschluss × 0,80 − Grundlast, max_grid_charge_kw) (Grundlast = Jahresverbrauch/8760, falls kein explizites Feld). Beispiel: 12 kW Anschluss + ~17 900 kWh/Jahr → ≈ 7,6 kW (nicht 12; unter Cap 8). Thin-Decide ohne battery.max_charge_kw im Body, sonst capped der Request die WR-Leistung — nicht den Profil-Cap.
  • PV-Größe (kWp) und Adresse/Koordinaten

Dann darf der Request dünner sein. Fehlt etwas in Request und Profil → Antwort incomplete_input („Daten unvollständig“).

Minimal-Beispiel (Decide)


curl -X POST https://api.spotpriceapi.com/v1/decision/hours-demand \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sf_live_YOUR_KEY" \
  -d "{
    \"timezone\": \"Europe/Vienna\",
    \"horizon_hours\": 168,
    \"constraints\": { \"max_hours_today\": 12 },
    \"battery\": {
      \"soc_pct\": 35,
      \"capacity_kwh\": 10,
      \"max_charge_kw\": 5
    },
    \"load\": { \"energy_next_24h_kwh\": 18 },
    \"pv\": {
      \"mode\": \"estimate\",
      \"lat\": 47.07,
      \"lon\": 15.44,
      \"kwp\": 6.0
    }
  }"

Ohne horizon_hours → Server-Default 168 (7-Tage-Vorausschau). Caps sind Slot-Anzahlen, nicht die Länge der Vorausschau. max_hours_horizon optional — ohne Override adaptiv aus dieser Speichergröße + Ladeleistung (ceil(Nutzband/kW)+12, max 168, kein Default 36). Response: max_hours_horizon_effective. basis.storage_horizon enthält nur Planungsmeta (Autonomie, Defer, Cap) — Jahres-€ und Sweetspot-Sizing nur in der Anlagenprognose.

Was bedeutet der Body?

TeilAlltagssprache
soc_pct: 35Batterie ist zu 35 % voll
capacity_kwh: 10Der Speicher fasst 10 kWh nutzbar
max_charge_kw: 5WR-/Request-Deckel 5 kW; published O11 = min(WR, Anschluss×0,80−Grundlast, max_grid_charge_kw) — Profil-Cap Default 8 (nicht im Body)
grid_surcharge_ct_kwhOptional: Anbieteraufschlag ct/kWh (Display-Kosten; Request → Profil → 1,5; 0…50)
energy_next_24h_kwh: 18„Heute brauchen wir grob 18 kWh“
pvSchätze den Solarertrag für diesen Standort

Antwort lesen

FeldEinfach
decision_statusok = Plan verwenden; sonst Fehlergrund
prefix_hours_needed_todayEMS: kontiguer Prefix heute — Extract O1 (0 wenn O9=0)
prefix_period_hours_todayEMS: Period für Prefix — Extract O2
prefix_energy_charge_kwhEMS: Plan-kWh für Prefix — Basis für O9 (Rest)
charge_now1 wenn aktuelle Stunde im Prefix und nicht latched (Live ≥ Stopp → 0 bis Live ≤ Stopp−H; Ausnahme: Live < min_soc−0,5 pp → Server öffnet Latch rearm_below_floor für Survival-Top-up; H = Profil-charge_hysteresis_pp Clamp 1–20 pp, Default 2; EMS-Feld charge_stop_hysteresis_pp). Engine v1.24+
charge_stop_soc_pctStopp-SOC am Prefix-Ende (%) — Extract O4. SoT aus floor(soc_projected_end_pct) nach Resolve-Refresh; kein live+energy-Invent (fehlt Projection → null, Latch kann prior sticky halten). Survival/Mindestladestand: Reserve-Floor (min_soc_pct) nur wenn der aktuelle SOC darunter liegt; sonst Prefix-Ende-Trajektorie / Session-Stopp (nie Stopp unter Live-SOC). target_soc_pct = Profil-Deckel, nicht O4. Explain/Sparvo: am gestempelten Prefix-Charge-Segment ist Timeline-Ende (soc_pct in ~a %→~b %) dieselbe int-Zahl wie Stopp-SOC / O4 — ein Zielwert.
charge_latched / charge_stop_hysteresis_ppv1.24 additiv (EMS-Prefix + Snap): sticky Latch / Hysterese-pp (Stopp−H). Unter Reserve-Floor (Live < min_soc−0,5) öffnet Decide den Latch (rearm_below_floor) — Survival-Top-up; oberhalb unverändert Hold bis Stopp−H. Fehlt Stopp/SoC oder Live/Stopp/min_soc als JSON-Bool und prior war latched → incomplete_hold / Floor-rearm skipped (Latch halten; nie float(True)=1). Client-Zahlen (load/pv Stunden, energy_next_24h, Constraints-max_hours*) und Stopp-Writer (Sticky-Republish / Snap / Webhook / Material-Key / Timeline) ebenso fail-closed. Kill HOURS_CHARGE_HYSTERESIS=0. Overlay display-only (kein privater charge_now 1→0). O12 bei Latch: kein recharge_planned-Vollcap — auch nicht wenn charge_now fälschlich 1 (fail-closed); Hold-Cap + Adaptive bleiben
Lookback-Thinv1.31: nach Lookback Drop-first/Relocate nur lookback-eligible kWh (auch unter reserve_breach; Sibling Weak/Cover frozen getrennt; Merge-lookback_added_kwh friert absorbierte Cover-kWh, coalesciert same-hour Siblings per geparster Stunde (naive→Map/start0/Vienna; Cover Soft-Cap-Floor; str/isoformat/naive sicher; Map-0/omit drop außer Interim-Pin Energie 0; lookback=True nur bei Δ>ε) und behält survival gegen Defer-Undo; Mixed cover_merged_kwh Frozen/Movable-Split und Drop/Relocate via split_cover_merged_on_relocate (Floor ≤ energy); rebuild behält Floor und weak_pv auf peelable Movable; Soft-Cap Stamp-Probe softcap_stamp_holds plan-level Cover/Mixed absolut / Weak-only Natural; Familie B Soft-Cap Natural nur bei Weak-Overlay ohne Cover-Fill und ohne Lookback-Movable; week_had_* überlebt PP-Forced-Re-Sim; Merge-Coalesce behält Cover Soft-Cap-Floor und MUST/interim/Spill/survival/Freeze-slot_role (prep/opp/bridge/dump + non-cover z. B. floor) vom Sibling; MUST setzt charge_kind=must; Absolute-Map drop’t unparseable/start-außerhalb Non-Spill; non-finite/JSON-bool Sibling-kWh → 0; Soft-Cap Identity/peelable/trim (jeder Exit inkl. Early-Hold-Scrub + In-Horizon soft_rel=None-Scrub)/softcap_stamp_holds/In-Horizon-Map/LThin-Map+lookback_added/Resolve Soft-Cap-adjacent Charge-Maps/Econ-PP Base+Frozen/Soft-Cap-Spill+clip/same-day Maps ebenso; Relocate-Split/cover_merged ebenso; Day-Consolidate _energy ebenso); Skip wenn lookback_applied=false; Stress-Delta-Konjunktion; Speicher-Headroom+Port wie Lookback-Place; Stress/s0 mit target_soc (Clip-Parität); Dest-Preis math.isfinite (−inf/NaN skip); Soft-Cap-Stamps = Weak-defer ∪ Near∪Dawn∪jedes sun→sun-plan.hi, auch Weak-aus / Familie-B Rebuild; eigenes Commit vor Thin-Cover-Defer; Residual-Note und lookback_reason_codes nur bei \(S≠∅\); Felder lookback_thin_dropped_kwh / lookback_thin_moved_kwh. Kill HOURS_LOOKBACK_THIN=0 (Default on). Decide + Familie B
Thin-Cover-Deferv1.31 (Feature ab v1.30): nach Lookback-Thin Relocate-only non-spill Cover/Weak auf günstigere Stunde in Near bzw. Weak-[lo,hi] (residual-aware Carry + Port + Speicher-Headroom min(cap[,target])/η + Partial-Port inkl. R2/_partial_port_take_ok); Dest-Preis finite (−inf/NaN skip); Dest meidet Spill ∪ Cap-Skip; Freeze Spill/Lookback/Survival/MUST/reserve_breach/Latch; incomplete Horizon → Carry refuse; Soft-Cap Stamp-Probe vor Dest (Cover/Mixed absolut via softcap_charge_rows_weak_only / Weak-only Natural, Consolidate-Parität; Stamps = Weak-defer ∪ Near∪Dawn∪jedes sun→sun-plan.hi, auch Weak-aus) + Post-Move Soft-Cap-Check → Revert + Weak-Trim (Trim-Σ-Guard → Revert, Neighborhood-Parität); Mixed cover_merged_kwh bei Relocate splitten/mitführen (sonst Soft-Cap-Natural-Reopen); same-flag Sibling-Merge; Probe-Fail ohne Move: Note thin_cover_softcap_dest_skip / Why; Post-Move Soft-Cap-Revert: Note thin_cover_softcap_revert / Why; ein Commit mit Thin-Now; Soft-Cap nicht reopen. Kill HOURS_THIN_COVER_DEFER=0 (Default on). Decide-only
Thin-Nowv1.24 / Carry-Heal v1.31: vor plan_roles nur slot_role=cover Now droppen wenn der Speicher bis günstigerem Slot heute residual-aware trägt (Netto > Room über Floor → carry_fail; incomplete Horizon → carry_fail); Prep/Opp/Survival/MUST (reserve_breach) nie; v1.25: Now mit softcap_spill (Spill/Darf-S1) nie droppen; Hold-Rebuild-Fail → Drop abort + prior Hold. Kill HOURS_THIN_NOW=0 (Default on)
Neighborhood Rebalance§6.8.18 Decide-only: nach Thin-Now Vorwärts-Move teurer Cover/Weak → später günstiger innerhalb Radius Default 18 h (Clamp 4…24, HOURS_NEIGHBORHOOD_RADIUS_H); Soft-Cap Stamp-Probe (Cover/Mixed absolut via softcap_charge_rows_weak_only / Weak-only Natural) + Post-Move Soft-Cap→Revert (+ Weak-Trim); Mixed cover_merged_kwh splitten/mitführen; same-flag Sibling-Merge; Stamps = Weak-defer ∪ Near∪Dawn∪jedes sun→sun-plan.hi, auch Weak-aus; Dest meidet Spill ∪ Cap-Skip ∪ Hold; Freeze wie Thin-Defer; orthogonal zu Econ-PP (rückwärts) und Thin-Defer (gleiches \(W\)). Kill HOURS_NEIGHBORHOOD_REBALANCE=0 (Default on, Dual-Gate User-Go). Familie-B follow-up
target_soc_pctProfil-Ziel-SOC (%) — nicht gleich Stopp-SOC
capacity_kwhSpeicherkapazität für SOC-Delta O9
prefix_charge_hours_todayKompakt-Array (start + energy_kwh) für Extract Slot-Match
hours_needed_todayPortal/Sparvo: Stunden heute noch im Period-Fenster (bis 23:00)
period_hours_todayEMS: Optimierer-Period = Reststunden bis 23:00 (00:00 → 23, 13:00 → 10, 22:00 → 1). Um 23:00 wieder 24. Nicht der Planhorizont. Mit Feature-Flag HOURS_INTERIM_ECON (Default an, Kill-Switch =0) kann sie kürzer sein (Cap 5 h), wenn ein imminent Zwischenladen (interim_period_active, Slot h < 5) vor der nächsten Billigstunde nötig ist — deferred Interim verkürzt die EMS-Periode nicht. Extract O2 liest prefix_period_hours_today (nicht dieses Feld).
period_hours_today_default(Flag an) Klassische Period bis 23:00 — Transparenz, wenn period_hours_today gekürzt ist
lookback_applied / lookback_passes / lookback_stress_remaining_kwh / lookback_placed_kwh / lookback_thin_dropped_kwh / lookback_thin_moved_kwh / lookback_placed_effective_kwhv1.23: Lookback-Rebalance nach Hold. v1.26: late-bias default on. Soft-Cap vor Lookback; lookback=True peel-exempt; Kill HOURS_LOOKBACK_REBALANCE=0. Passes Default 4. v1.31: Place Speicher-Headroom + Lookback-Thin; Kill HOURS_LOOKBACK_THIN=0. v1.36: Dawn-Scope O2 first contiguous Stress-Cluster + O5b Far-JIT 36 h (HOURS_LOOKBACK_DAWN_SCOPE, Kill =0 → Pre-Heal Identity); Fill-Day-Block Survival auf Fill-PV-Tag außer S_work ⊆ demselben sun→sun (HOURS_LOOKBACK_FILL_DAY_BLOCK, Decide + Familie B); Far Soft-Cap-Kohorte + Natural-Probe; Tages-Stempel-Probe öffnet bei ≥5 €/MWh unter der Nacht ohne Extra-Abregelung (HOURS_LOOKBACK_FAR_SOFTCAP_GUARD; Soft-Cap-Stamps = Weak-defer ∪ Near∪Dawn∪jedes sun→sun-plan.hi; Familie B dawn_cap=None → Guard no-op). Top-level Decide-Echo (additiv): lookback_placed_kwh (Place-Σ), lookback_thin_*, lookback_placed_effective_kwh = Σ charge_hours[].lookback=True (final SoT; nicht Place−Drop). Snapshot-Parität. reason_codes: lookback_rebalance / lookback_residual_stress / lookback_fill_day_block / lookback_far_softcap_guard
charge_hours[].lookbackv1.23: Slot vom Lookback-Ping-Pong (survival=true, slot_role=cover); Soft-Cap-Exempt
softcap_abandon_spill_applied / softcap_abandon_spill_placed_kwh / softcap_abandon_spill_skipv1.26 default on: Soft-Cap-Abandon-Spill nach Near Soft-Cap-Abandon — Rest in S1. v1.31: Speicher-Headroom upper−soc_i−already·η (Lookback/Defer-Parität; kein Plan-Decken-Overshoot); Partial-Port/R2 auf vollen Inverter-Port (Cover-Parität). Kill HOURS_SOFTCAP_ABANDON_SPILL=0 (Felder fehlen). Skip → applied=false + skip (autark / no_grid / no_power / no_horizon / no_abandon / no_next_stamp / empty_cohort / euro_cap / clipped / echo_error). reason_codes: softcap_abandon_spill. Decide=SoT; Echo top-level + basis / Snapshot / plan_feasibility
darf_softcap_s1 / darf_softcap_s1_placed_kwhv1.26 HOURS_DARF_SOFTCAP_S1 default on: Darf Near=0 → S1-Spill-Cohort (Gate OR). reason_codes kann darf_softcap_s1 enthalten. Kein S0-Fallback. Thin-Now droppt Spill-Now nicht. Interim ¬breach → darf_softcap_s1_skip_near. Kill =0 → Near-only
charge_hours[].softcap_spillSlot aus Soft-Cap-Abandon-Spill (slot_role=cover; nicht Lookback-Exempt; Day-Consolidate/Rebalance/Lookback-Place fixiert; Prep/Opp/Far kein Top-up; Weak-Apply behält Flag; Thin-Now und Thin-Cover-Defer behalten/rühren Spill nicht)
interim_*(Flag an) Econ-Bridge: interim_needed, interim_decision (charge\
hours_neededVoller Plan über den Horizont (Cover + optional Dump + Vorrat). Portal/Sparvo zeigt das getrennt von hours_needed_today (Chip „heute … h · insgesamt … h“)
energy_charge_kwhSoll-Netzbezug in kWh, auf die nächste ganze kWh aufgerundet (1,4 → 2). Enthält den Cover-Puffer (Default 10 % auf den Fehlbedarf; Portal Standort / Sparvo Einstellungen → Speicherregeln). Nicht max. kW × Stunden.
hours_needed_daysImmer 7 Zahlen [heute, D+1, …, D+6] (nie null). Extract v1.23 (O9-Headroom bis Session-Stopp/O4; Latch → Rest=0): O5 = PulseSessionStop; O6–O8 frei (Portal/Wochenblick). UI Wochenblick: Index = Kalendertag D0…D6 — nicht der Index in der sparse day_plan-Liste. Eine Zeile mit 0 kWh zählt nicht als geplante Stunde, setzt charge_now nicht und ist nicht die nächste Ladung. Ein älterer Snapshot wird in der Anzeige genauso gezählt, auch im Wochenblick, in den Tageschips und in den Explain-Uhren der Anlagenseite. Die Zuordnung startet am Wiener Ausgabetag. Eine Uhr ohne Energie-Feld bleibt eine Ladestunde.
next_charge_start / next_charge_hoursNächster verbleibender Slot (ab jetzt) plus Stunden an diesem Kalendertag.
grid_charge_allowedTop-Level (EMS): Master-Schalter true/false — Loxone Extract O10. Gleicher Wert in basis.grid_charge_allowed.
energy_deficit_kwhBedarf nach Platzierung (plan_feasibility.energy_requested_kwh, ohne leftover Prep). Sized Prep-Floor bleibt in basis.forward_prep
charge_power_kwVerfügbare Netzladeleistung in kW — Extract O11 (nicht die Roh-Netzanschlussleistung); Formel min(WR, Anschluss×0,80−Grundlast, max_grid_charge_kw) Default Cap 8 (hours_demand_v1.36)
discharge_power_kwEntlade-Cap in kW — Extract O12; fehlend → PicoC-Fallback 9. Surplus-Cap Hold (v1.20): Cover-Lücke → Off-Peak-Defizit vor Peaks in Hold mit lastgewichtetem Slot-Cap (hold_share / pack_reserve 0…0,2); Now ∈ Hold-Set → O12 = Slot-Cap — ohne zweiten Cover-Gate. SoC≤min in Hold → O12=0 (reserve_empty) vor Cap. Peaks (Abend/Morgen) sind nicht im Hold und folgen derselben Rampe. Near-Autark: kein Hold in \(H_{gate}\); Folgetag-Hold/Weak ok. Nicht bei vollem Speicher (≥99 %) und nicht in einer Stunde mit PV-Überschuss. Stretch/Reserve-Lücke allein reicht nicht. Während aktiver Netzladung (charge_now und nicht latched/grid-aus) volle Cap nur ab der Kurven-Oberkante; darunter die Rampe. An oder unter dem Mindeststand ist die Entladung 0, auch wenn PV-Überschuss oder charge_now den Hold überspringt. Im genommenen Hold über dem Mindeststand bleibt die Slot-Cap. Latch/Grid-aus → kein Vollcap (auch bei stale charge_now=1), Hold-Cap bleibt wenn Now ∈ Hold. Adaptive sonst nur außerhalb Hold: volle Cap ab der Kurven-Oberkante (min + 15 % des Bereichs darüber, bei Mindeststand 20 % ab 32 %), darunter sinkt und steigt die Entladung mit dem Speicherstand, auch bei geplanter oder aktiver Ladung. 0 an oder unter dem Mindeststand, auch wenn später geladen wird, oder bei User-Cap 0
min_soc_pctMindest-Speicherstand (%) — Extract O13
adaptive_dischargetrue = Reserve-Rampe unter der Kurven-Oberkante, auch bei geplanter Ladung; 0 an oder unter dem Mindeststand; false = festes max_discharge_kw (Portal Standort / Sparvo Speicherregeln). Unabhängig vom Surplus-Cap-Hold-Set
discharge_reasonWarum O12 so steht: cheap_hold · hold_share · pack_reserve · headroom · recharge_planned · reserve_hold · reserve_empty · adaptive_off · soc_unknown · user_off · ev_charge_block (Wallbox lädt, Entnahme 0 %) · ev_charge_limit (Wallbox lädt, Entnahme × Sparvo-%)
cheap_take_hoursN = ceil(Tageslast / Lade-kW), max max_hours_today (Default 12). Tageslast = Typical heute, sonst Jahres/365. Nur Preis-Signal / Legacy-Cheap-Clock — nicht das Surplus-Cap-Hold-Set (hold_hours)
hour_is_cheapaktuelle lokale Stunde ∈ günstigste N (cheap_take_hours) — unabhängig von Hold-Mitgliedschaft. Teure Off-Peak-Hold-Stunden können false sein, während O12 trotzdem Slot-Cap ist
cheap_holdtrue nur wenn discharge_reason=cheap_hold (Weak/Legacy ≈1 kW). Bei hold_share/pack_reserve = false
hours_needed_cover / hours_needed_prep / hours_needed_opp / hours_needed_bridgeAufschlüsselung: Cover · Dump (Opp) · Vorrat (Prep) · Bridge — Picker-Placement Near-Cover→Soft-Cap-Abandon-Spill (Flag/Darf-S1)→Prep→Far-Cover→Bridge→Opp (Sizing ≠ Placement, siehe §14.6)
prep_extra_energy_kwhPlatzierte Prep-kWh (slot_role=prep, oft 0). Der sized Floor bleibt in basis.forward_prep
charge_hours_today / charge_hoursStarts heute bzw. voller Plan (v1.3: nur SOC-taugliche Slots). Intern kann die Engine gleiche-Tages-kWh auf eine günstigere ladebare Stunde umlegen (P1-v2, Server-Flag; abgelaufene Stunden bleiben). v1.31 heal: Accept-Trough/Emit load-first; Clip upper=min(cap,target). Response-Shape unverändert, kein neues Pflichtfeld, discharge_reason / O12 unverändert. start/end sind ISO-Strings — interne datetime-Uhren (Lookback/Survive) werden vor Wochenblick, Snapshot und JSON umgewandelt. Der Wochenblick zählt die kWh auch bei datetime-Text mit Leerzeichen
plan_feasibilityok / partial / no_feasible_slots — geplante vs. untergebrachte kWh; storage_undersized; Soft-Cap-/Dawn-Abandon in energy_abandoned_cover_kwh (zählt nicht als Undersize)
skipped_slots[]Günstige aber unmögliche Stunden (skip_reason: u. a. soc_at_capacity, pv_self_charge_sufficient, pv_will_fill_today, pv_fills_charge_power) — immer mitgeliefert; Portal/Sparvo Netzladung listet sie chronologisch mit Datum. Mehr als 6 Zeilen: Link auf /plant/skipped-hours bzw. /plant-outlook/skipped-hours (Owner-only). Warum-Details: /plant/hours-why bzw. /plant-outlook/hours-why (Mitbenutzer wie Explain). Vollliste Skip: Stundenzeile ohne „Nächste Ladung“; einmal pro Tag eigener Bullet „Nächste geplante Ladung“ (bzw. „keine weitere Netzladung“). pv_will_fill_today: Skip-Gate in Grid-kWh (pack_leftover/η ≤ min_headroom) — Cover/Lookback-Parität
charge_hours[].soc_projected_* / slot_role / survivalErklärung pro Slot (Cover/Prep/Opp, SOC vor/nach). Portal/Sparvo: günstige Lade-Stunde · Vorratsstunde · Erhaltung Mindestladestand · günstiges Netzladen · Übergangsladung · Netzladung (Schwach-PV)
day_planPro Kalendertag: N + Starts (Portal/Sparvo); ab v1.14 inkl. hold_hours[] (Hold / Speicherreserve, auch hold-only Tage)
hold_hoursHorizont-Hold-Slots (start, hour_local, discharge_cap_kw, reason=hold_share\
direct_grid_hold_kwhGeschätzter Netzimport in Hold-Stunden (Last − PV − Slot-Cap)
basis.reserve_breach / basis.econ_charge_ok / basis.charge_kindv1.16: MUST = Reserve-Bruch; Darf = günstiges Drittel; weak_pv = Rentable Day-Fill (Sonne→Sonne, η-Gate); charge_kind ∈ must/darf/weak_pv/interim/prep/opp/none. Endnutzer: nie „P33“ — Phrase „günstiges Drittel“ nur bei darf; Weak-PV: „Nachladen wenn nach Wirkungsgrad günstiger als teurer Abend-Direktbezug“
basis.week_statusv1.15+ Copy B: lines nur für Plan-Tage (Hold/Charge/Charge+Hold) — Autark/Idle/no_hold_gap/opp_* nicht in der Liste (days[] behält alle). Heading Do 10.09.: (Wochentag + dd.mm.). Hold: eine Uhrliste mit HH:00 X,X kWh oder ohne Bezug · Summe = angezeigte 1‑dp‑kWh; Wortwahl Speicherschonen / kein Speichern aus dem Netz. hold_direct nur wenn Anzeige‑Summe > 0 (Mikro‑kWh entfallen). Idle mit Restlast → no_hold_gap (schlägt Near-no_charge_covered/interim_pass, nicht opp_*). why_code u. a. autark, no_hold_gap, hold/hold_direct, charge_*, no_charge_covered, grid_off, interim_pass, opp_off, opp_not_triggered. Hold ohne Import = Speicherschonen (Slot-Cap HOURS_HOLD_KW oder Speicherreserve HOURS_PACK_RESERVE_KW). Cover schlägt Interim-Pass-Hint. Jeder Decide neu über [now, now+H≤168). Kein Gruppen-„Netzbezug:“. Portal/Sparvo: Explain zeigt zwei Folds (Netzladung + Bis morgen früh·Folgetage) mit plan_timeline im Netzladung-Fold (keine separate „Diese Woche“-Prosa; API-Feld bleibt); Wochenblick: kurzes Teaser-Echo; Tageschart Tip Charge > Hold
basis.weak_pv_active / basis.weak_pv_dpWeak-PV-Mix aktiv; DP nur nach DA-Replan
charge_hours[].dump_risk_pct / dump_scenario_ct_kwhAus der Forecast-Stunde: pct 0…100; Scenario = Dump wenn pct > 0, sonst derselbe Punktpreis wie price_ct_kwh (immer nutzbar, an die Zeile angeglichen) — rein additiv, Planberechnung nutzt immer den Modellpreis
basis.cover_window_hours / remaining_today_load_kwhLänge des Cover-Fensters (ggf. Reserve-Stretch); Rest-heute-Last (Typical + Same-Day-Overlay minus Ist). Daneben cover_window_hours_pv / cover_extended_for_reserve / cover_recovery_ok / cover_dawn_cap_hours / energy_cover_dawn_kwh (v1.13: Recovery + Dawn-Cap an hi(next_pv); Cover-Defizit-Response = Dawn-geclippt)
basis.forward_prep / basis.prep_horizon_hours / basis.carry_hoursVorrat v1.8/v1.11: Extra-kWh, \(G_{min}\), Cap/P20/Absolute, gap_day_ranges, Datenfenster \(H_{prep}\), Trageweite \(H_{eff}\). v1.11: nach Cover-Pfad gesized (notes ggf. sized_after_cover); energy_kwh ≤ v0. Prep-Gap = PV-Tag; EMS-Felder = Kalendertag
basis.autark_ok / basis.autark_notesv1.10+: PV+Speicher reichen im Gate-Fenster \(H_{gate}\) = Rest-heute ab Decide + nächster Kalendertag bis Morgensonne → keine Near-Netzladung (Cover/Prep/Dump/Bridge = 0). Weak-PV (§6.8.15) darf nach \(H_{gate}\) weiter laden (kein Wochen-Skip). Ausnahme: cover_extended_for_reserve → Stretch-Cover (stretch_reserve_outside_gate). Dual-Contract: Engine = Sonne→Sonne; day_plan = Kalender 00:00–23:59; \(H_{gate}\) nie Mitternacht. autark_notes: gate_h:… oder Trog-Hinweis. Alle Anlagen/Keys. Kill-Switch HOURS_AUTARK_GATE=0 (Default an)
basis.grid_charge_allowedMaster-Schalter aus dem Anlagenprofil (Default an). Aus → alle hours_needed* 0, leere Slots, charge_kind=grid_off, charge_now=0. Server sofort; EMS sofort nur mit plant.plan_updated (sonst nächster Hours-Decide). Auch bei incomplete_input / invalid_input (fail-closed false wenn unbekannt) — EMS-Prefix defaultet nicht still auf true
basis.hours_control_paused / pause_reasonCold-Start / Warm-up (hours_demand_v1.36 Dual-Cycle): true solange Lastprofil nicht ready (Meter-Warming oder Annual-&lt;72 h). Meter ready unter Dual (HOURS_COLD_START_DUAL_CYCLE default on): Kalendertag ≥18/24 Laststunden und erster abgeschlossener PV-Tag sun→sun (Lastanteil ≥50 %; PV Meter densified oder Geo-Hist PAST_DAYS=7 cache_only — nie Open-Meteo-Refresh aus Dual Decide). Kill Dual=0 → Typical-only; Total-Kill HOURS_COLD_START_PAUSE=0. OK-Publish mit leeren Charges/Holds, charge_now=0, Latch false, charge_stop_soc_pct: null (Snap+Webhook explizit), O12 Max/at_min (+ EV-Flex ev_charge_block/ev_charge_limit), charge_kind=none, week_status why_code=cold_start, load_covered_by_soc_pv=false, Outlook/empty_reason=cold_start (Wochenblick D0 Warm-up; Explain Chip/Headline Warm-up, nicht Gedeckt/chip_none); Pause ∩ Grid-off → Pause gewinnt (reason_codes=cold_start_pause, UI charge_kind=none, week_status why/empty_hint/groups → cold_start nie grid_off/Hold/Gedeckt — Display-Strip folgt hours_control_paused Top-Level/Basis, nicht sticky notes allein; Pause mit Netzladung noch an: gleiche Mute ohne grid_off; kein Mit-Netz-SoC; Outlook Snapshot-First und Live-Hydrate setzen hours_needed*/energy_charge_kwh/next_charge_* fail-closed auf 0). Nur Grid-off: half-healed status=charge mit Hold-Evidence → Speicherschonen (hold), nicht Autark. pause_reason: cold_start_meter_warm \
basis.pv_fill_lookaheadLook-ahead-Audit: Near/Survival discount 0,70; Prep/Far-Cover discount_prep_far 1,0; fills_safe wenn SoC frisch
basis.hourly_load_cover_kwh / hourly_pv_cover_kwhStündliche Last/PV im Cover-Fenster (ab jetzt) — Portal/Sparvo Zu Planende rollt SoC chronologisch (kein Bulk PV−Last); Überschuss bei vollem Speicher clippt
basis.load_refinev1.9: Metadaten Kurzfrist-Refine (kind: partial_hour / meter_budget / recent_rate / client_override; partial_hour_frac; recent_hours_used; skipped bei Client-Last). Folgetage unverändert
soc_pct_at_decide / projected_soc_pct_*_at_decideOptional: Decide-Zeitpunkt-Werte wenn Live-SOC nach Telemetrie auf „Jetzt“ gemappt wurde (soc_live_overlay)
basis.hourly_grid_charge_cover_kwhGeplante Netzladung je Cover-Stunde (Indizes wie Last/PV) — für Planende „Mit Netz“ ohne Zeit-Remapping
basis.max_hours_horizon / basis.max_hours_horizon_effectiveCap für vollen Plan (hours_needed). Optional im Request — ohne Override aus dieser Speichergröße + Ladeleistung (need_h+12, max 168). effective = tatsächlich genutzter Cap. max_hours_today (Default 12) wird auf diesen Cap geklemmt.
basis.storage_horizonNur Planungsmeta (Autonomie, Defer, Cap). Jahres-€, Sweetspot und savings_vs_* sind null — die stehen in der Anlagenprognose (Amort-Hardware dort 160 €/kWh netto × 1,20, fertig eingebaut). Die Jahres-Ø dort ist Netzladung plus übriger Bezug aus demselben Lauf. Das Niveau folgt dem 90-Tage-Spot-Regime (70–150 % des 12-Monats-Mittels); die Ø-Chips 12 Monate und 90 Tage bleiben die rohen Spot-Fenster. Kill STORAGE_HORIZON_PRICE_REGIME_SCALE. (Hybrid-UI + Outcome Guard: Toggle Nur-PV / Mit-Netzladen, Dual-Annual, Titel/Cap/€ aus display_outcome_*, Kill STORAGE_HORIZON_OUTCOME_GUARD default on — siehe Anlagenprofil-Konzept). Fixtarif-Kachel (E-Control, Formular) nur Portal/Sparvo Outlook. Abfrage = Netz-Restbezug in 100-kWh-Stufen, nicht der Jahresverbrauch. Gespeichert wird der Energiepreis nur, wenn er unter dem Gesamtpreis der Cent-Spalte liegt; eine Euro-Jahreszahl wird nicht als ct/kWh übernommen. Die Kachel mit E-Control-Branding weist darauf hin, dass Alter, Neukunde und Aktion beim Anbieter stehen. Der gespeicherte Tarif lässt eine Altersgrenze, eine Verbrauchskappe unter 100.000 kWh/Jahr und einen Tarif nur für Neukunden aus, nur im Produktfenster. Die Firmenstartseite zählt nicht. Preisgarantie für 12 Monate, die Staffel bis 100.000 kWh und ein Neukundenbonus bleiben. Fehlt nach einem fertigen Lauf ein zulässiger Festpreis, zeigt die Stufe 11,4 ct ohne diesen Hinweis. Ein Fehler lässt die alte Zeile stehen. Kill ECONTROL_TK_HOUSEHOLD_ONLY=0. Fehlt Energie, Gesamt oder Anbieter, wird die PLZ sofort abgefragt, auch innerhalb von 23 h, und nicht ein zweites Mal solange der Lauf läuft; die Kachel zeigt dann „wird abgefragt“ statt 11,4 ct. Der tägliche Abruf holt höchstens eine Stufe pro Stunde, zu einer festen Stunde aus PLZ und Stufe. Der Outlook-Cache (120 s) liest diese Zeile beim nächsten Aufruf neu. bis einschließlich N Jahre ab 18 und N- bis M-Jährige sind eine Altersgrenze, auch neben einer Preisgarantie. Die Cent-Liste ist Netznutzung, Netzverlust, Netz-Grundpreis, Messentgelt und die Abgaben. Der Energie-Grundpreis steht als €/Monat. Gesamt ist die Jahreszahl der Stufe. Der Vergleichs-Chip bleibt Energie gegen Dynamik. Energie und Grundpreis sind netto; Brutto nur, wenn der MwSt-Schalter der Preisseite an ist (Expand: MwSt-Hinweis + Anbieterlink changeLink.link) — Hours Decide holt keine Tarifkalkulator-Daten (Compare-Import fail-closed finite Env/ct; Opt-out nur Outlook). Outlook-Payload (nicht Hours Decide): sweet_spot_pv_kwh / sweet_spot_bridge_kwh / recommendation_mode + Dual-Annual annual_grid_cost_{pv,bridge}_eur + display_outcome_{pv,bridge} (Kill STORAGE_HORIZON_DUAL_ANNUAL default on); aktive Bridge-Empfehlung host-weit mit STORAGE_BRIDGE_SWEETSPOT=1 (User-Go 17.09.2026), Code-Default weiter off.
basis.charge_defer_enabledtrue = Mehrtage-Arbitrage (global günstige Stunden über Cover); große Packs → höherer effektiver Horizont
missingWelche Angaben fehlen (bei incomplete)

Schwach-PV / Rentable Day-Fill (v1.16)

An Schwach-PV-Tagen (Last−PV bis zur nächsten Morgensonne größer als nutzbarer Speicher) plant die Engine Tag-für-Tag über 168 h (Rentable Day-Fill ab hours_demand_v1.16, Surplus-Cap Hold ab hours_demand_v1.20 — Kill HOURS_HOLD_CONTINUOUS_CAP=0 → Cap0/Liberate wie v1.18; PV Cap-Skip ab hours_demand_v1.27, Audit v1.28/v1.29): Netzladung nur wenn Börsenpreis/Wirkungsgrad günstiger als späterer teurer Direktbezug (p/η < p_ersetzt − ε), Ziel = Defizit sun→sun (need_full = Summe positiver Netto-Stunden, Surplus reduziert das Defizit nicht). Cap-Skip: reicht PV-only (ohne Hold) bis Cap vor Abend und ohne Residual-Import → need_full=0, keine Weak-Netzladung (Note weak_pv_need_full_pv_cap_skip; Kill HOURS_WEAK_PV_NEED_FULL_PV_CAP_SKIP=0 → Legacy-need_full-Fill; ab v1.28 kein DA-DP nach Cap-Skip; ab v1.29 Lookback blockiert Cap-Skip-Fenster). Sonst Zuweisung in W preisoptimal unter Band ∩ η ∩ Port>ε (SoC-Roll; net≤0 blockiert Grid nicht bei Port>ε), Hold/Conserve auch ohne Ladung (hold_share/pack_reserve/cheap_hold), Soft-Cap nach Inject (Natural-Baseline max(min+10 pp, natural[stamp]) — peelt nur Mehr-Laden; Cover Soft-Cap bleibt absolut; fail-closed Trim ab plan.lo inkl. plan.hi/Dawn, Post-apply Stempel = Near/Dawn ∪ Weak-plan.hi; Peak-Cap-Refresh immer nach finalem Fill inkl. DP/\(H_{gate}\)/Soft-Cap — auch Empty-Fill nach Gate-Wipe + SoC-Resim Tag→Tag; Hold-Energy-Map immer Dict ({} = Key 0)). Cap0/fill_before (Kill-Switch-Pfad): bei gesetztem charge_kwh_by_index nur gemappte kWh (fehlender Key = 0). Familie B: gleiche Natural Soft-Cap am sun→sun-Ende (ohne Dawn-Kill-Gate; Peel nur Kalender-h < hi, Post-Mitternacht deferred + end_soc-Resim; Cover/Prep nur wipe’n wenn Kalender-Weak existierte) + Cap-Refresh + Cover/Prep-Energy-Map. Relocate: Weak-only Soft-Cap-Check = Natural; Cover/Mixed absolut; Mixed weak_pv-Budget mit Split der letzten Stunde (Cover-Rest ohne weak_pv, Sibling-Rows gleiche start; hours_needed/EMS-Prefix = unique Starts; Compact-EMS merget Sibling-Energie). Near-autark_ok skippt Weak nicht — Filter i < H_{gate}, dann Resim, danach Soft-Cap mit Dawn/> H_{gate} (Near-Stempel ≤ \(H_{gate}\) nicht). Redundant-Peel (Default an): Weak-Netz-kWh, die im selben Sonne→Sonne-Fenster den Haus-Restimport nicht senken, fallen weg (typisch der Port-Rest auf einer Sonnenstunde). Die Messung benutzt das ausgespielte Hold-Cap, nicht die volle Entnahme. Ein Speicherkauf, der den späteren Hausbezug in diesem Fenster nicht senkt, fällt weg. Ein Kauf, der einen späteren Import noch trägt, bleibt. Nötige günstige Stunden davor bleiben. Danach werden die Holds zum verbleibenden Fill neu geplant. Kill HOURS_WEAK_PV_REDUNDANT_PEEL=0. Forward-Rentabilität (Default an): dieselbe Messung, aber am Preis — die Ladung bleibt nur, wenn die vermiedenen Hausimporte teurer sind als der Kauf nach Wirkungsgrad (ε 3 €/MWh, kein Aufschlag). Ein flacher Preis-Schwanz nach einem schon gedeckten Abend fällt weg (Note weak_pv_forward_unrentable; Kill HOURS_WEAK_PV_FORWARD_RENT=0). Ein Neighborhood-Move, der diesen rentablen Rest verkleinert, wird zurückgenommen (neighborhood_forward_rent_revert). API-Slots bleiben Kalendertag 00:00–23:59. charge_kind=weak_pv nur wenn Weak-PV-Ladestunden gesetzt wurden. Hold-only-Fenster strippen keine Darf-Cover; EMS-hold_hours ohne parallele Charge-Stunden. Optionaler DP-Feinschliff nur im bestehenden Day-Ahead-Replan (allow_weak_pv_dp; Kill HOURS_WEAK_PV_DP), η-gefiltert und auf need_full begrenzt. PicoC unverändert (nur Live-O12 / charge_now). max_hours_today Default 12 bleibt — der Horizont plant weiter voll; EMS/Today-Slice sieht höchstens 12 Today-Slots. Gilt für alle Anlagen/Keys.

Econ-Ping-Pong (Default ON): Nur Weak-kWh. Plain Cover zieht im selben Sonne-Fenster auf die frühere billigere Stunde (HOURS_COVER_EARLY_PLACE, Default an), nach Neighborhood und vor der Direktplanung. In der Jahresrechnung steht eine dunkle Vorspanne vor der ersten Sonne nicht vor einer späteren billigeren oder gleich teuren Stunde desselben Tages. Eine spätere teurere Stunde springt nicht vor diese Nacht. Diese Ladung bleibt nur, wenn sie nach Wirkungsgrad die Preise schlägt, die sie bis zur nächsten Sonne oder im Rest des Tages vermeidet (ε 3 €/MWh); sonst importiert das Haus direkt. Der EMS-Plan folgt dieser Reihenfolge nicht. Übernimmt ein Schwach-PV-Plan den Tag, gilt dessen Reihenfolge. Eine schon eingefrorene Zeile auf dieser Stunde bleibt; die verschobenen kWh liegen daneben, solange der Anschluss frei ist. Der Chip „keine frühere legale günstigere Ladestunde für Schwach-PV“ kommt nur aus dem Weak-Pass. Nach Weak-Mix können nötige Weak-kWh auf frühere günstigere Slots verschoben werden (move≠add; η/RENTABLE_EPS; Soft-Cap-Ceiling = Plan-Ziel, nie Cap; Dest ∉ Cap-Skip/Occupied/Hold/Near-Gate — Familie B: prior Near‑Fenster zusätzlich Occupied; Forced-Apply hält intended per-hour Caps). Stamp-Probe: Cover/Mixed absolut / Weak-only Natural (softcap_stamp_holds, frozen Cover in Port/SoC/Stamp — Thin/NB-Parität); unsichere Dest → Note weak_econ_ping_pong_softcap_dest_skip nur wenn kein erfolgreicher Move (sonst drop wie Neighborhood); Peel nach Move → Revert bleibt Safety-Net. Cover-Merge: Post-apply Soft-Cap peelt nur Weak-Rest (cover_merged_kwh-Floor) und wählt Mode plan-level via softcap_charge_rows_weak_only (Weak-only Natural / Cover/Mixed absolut, nie immer-Natural); Peel-Exempt lookback/survival/MUST (softcap_may_peel_row). Day-Consolidate Weak-Budget = nur peelable Weak (softcap_weak_peelable_kwh, Floor ausgenommen; Cover-Crumb < min_head behält cover_merged_kwh → Mixed absolut); Soft-Cap-Gate immer (Cover-Δ Soft-Cap-Break → Reject; Soft-Cap schon broken → Cover Soft-Cap-Worsen pre-stamp-Σ↑ → Reject; Survival-Δ darf Soft-Cap-Breach). Soft-Cap-Demote clamp cover_merged≤energy. SoC-Pfad Soft-Cap: roll_soc_hour/forward_soc_path/Stamp/Lookback/O4/plan_roles/NB+Thin residual/Weak Soft-Cap end_soc-Resim/Econ-PP Soft-Cap SoC/Soft-Cap-Spill-€ scrubben JSON-bool/NaN→0 (kein float(True)=1). Kill HOURS_WEAK_EARLY_PLACE=0. Decide + Familie B. Survival-Lookback (14b) bleibt getrennt.

Neighborhood Rebalance (Default ON): Nach Thin-Now können teure frühe Cover/Weak-kWh auf später günstigere Stunden innerhalb Radius \(R\) (Default 18 h) verschoben werden (move≠add; Carry residual-aware; Soft-Cap Stamp-Probe Cover/Mixed absolut plan-level / Weak-only Natural + Post-Move Soft-Cap→Revert). Orthogonal zu Econ-PP (nur rückwärts) und Thin-Defer (gleiches \(W\)). Kill HOURS_NEIGHBORHOOD_REBALANCE=0. Decide-only v1; Familie-B follow-up.

Explain plan_why_chain: Portal/Sparvo zeigen Bürger-Chips aus Decide-Notes (Autark; ¬Darf nur ¬Autark∧¬Weak; Cap-Skip; Forward-Rentabilität; PP move/revert/dest_skip — dest_skip nur ohne Move; Lookback-Thin; Neighborhood move/softcap_dest_skip/revert/carry; Dual-Contract nur D0=0∧später>0). Additiv im UI-Payload — PicoC unverändert. Decide top-level Thin-Echo: lookback_placed_kwh / lookback_thin_* / lookback_placed_effective_kwh. Explain-UI: zwei unabhängige Folds — Netzladung (Plan/Why) und „Bis morgen früh · Folgetage“ (Nacht + Folgetage), beide default zugeklappt. Warum-Details (Phase 1 + Lite): Sibling-Link „Details“ neben dem Week-Teaser → Sparvo /plant/hours-why · Portal /plant-outlook/hours-why?api_key_id= — Snapshot-Narrativ (use_live_resolve=False), Mitbenutzer wie Explain (nicht Owner-only wie Skipped-Hours); Kill HOURS_WHY_DETAILS (Default on). Hero/Rollenfarben/FAQ; Segment-later_code (Start-SoC Floor; Soft-Cap-Hint ohne Doppel-Later; Softcap-Move/no_legal_later + Dedupe); Bürger-Copy Speicherschonen/η. CSS-Pin hx30. data-why-details armiert bei Kill+Href (auch Shell); Sichtbarkeit nach SSR//data-Hydrate wenn available. Kein Re-Decide, kein Counterfactual; PicoC/Decide-JSON unverändert.

Cover vs Dump vs Bridge: Ist Netzladung erlauben aus (grid_charge_allowed=false), sind alle Stunden 0 — unabhängig von Cover/Dump. Explain-Kind dann grid_off / Reason grid_charge_disabled. Heute 0 / Woche > 0 bleibt later — Mit-Netz am Reservestempel bleibt sichtbar. Mit ausgeschaltetem „Günstiges Netzladen“ (Dump/Opp off) und gedecktem Cover ist hours_needed = 0. hours_needed_bridge entsteht nur, wenn Dump/Opp an ist — kein künstliches Netzladen „auf Verdacht“. Positiver Cover-Fehlbedarf bekommt den Cover-Puffer (battery.cover_buffer_pct bzw. Profil, Default 10 %). Getrennt davon: Relais-Ladehysterese battery.charge_hysteresis_pp (Default 2, Clamp 1…20) → EMS charge_stop_hysteresis_pp / sticky Latch (Kill HOURS_CHARGE_HYSTERESIS=0). Cover/Bridge/Survival/Lookahead/Lookback/Consolidate/Interim/Spill/Rebalance planen unter der Decke min(cap, target_soc). min_soc_pct / target_soc_pct liegen in 0…100 (sonst invalid_input); Defaults 20 / 90. Einstellen: Portal Standort / Sparvo Speicherregeln. Loxone kann den Schalter per Telemetrie setzen ({"grid_charge_allowed":0|1}) und den Stand auf Extract O10 lesen — siehe Loxone Hours.

Geplanter Netzbezug Haus + Anbieteraufschlag (seit v1.19)

Seit hours_demand_v1.19 (Rollen/Explain-Polish unter Engine hours_demand_v1.36): nach finalem Surplus-Cap/Hold klassifiziert Decide Residuen als geplanten Haus-Netzbezug nur wenn die Stunde kein Speicherladen ist, spot ≤ P33(Kalendertag) und spot ≤ p_ersetzt ohne Wirkungsgrad-Faktor (Hausbezug ≠ Speicherladen). Explain-Netzladung zeigt Kalendertag-Rollen (Netzladung / Speicherschonung / PV + Speicher / Speicher leer / Netzbezug Haus) mit Fuß X/Y/Z (Spot-Kosten nur X/Y/Z; Payload spot_cost_eur + tariff_markup_ct_kwh). Anbieteraufschlag (grid_surcharge_ct_kwh, Default 1,5 ct/kWh netto ohne MwSt; Resolve: Request → Profil → Default; 0 bleibt 0) ist der Betrag — ob er (und 20 % MwSt) in Portal/Sparvo erscheint, steuern Account-Prefs display_markup_on/display_vat_on (Default aus = Börse netto; Guest/Admin: Browser-localStorage). Die öffentliche Decide-/Hours-JSON enthält keine Prefs-Flags. Nicht in η/P33/Weak/Direct-Gates. Timeline-Ops alle Kinds: (PV, Speicher, Netz, SOC) — {soc_pct} = fertiger Token (~a %→~b % Start→Periodenende, Ende ~b %, oder —); additiv soc_start_pct; soc_pct bleibt Periodenende. Shared Token-Builder (SSR/format_plan_soc_token + JS). Bei Rolle Netzladung ist Netz die mittlere (Lade-kWh + Haus-Rest), nicht nur der Haus-Rest. Live-Overlay schreibt Timeline-SOCs nicht um. Wochenblick-Why-Energie = Horizont-Summe X+Y+Z. PicoC/day_plan unverändert. Gilt für alle Anlagen/Keys.

HOURS_ECON_LEAD_BAND (Default an, User-Go 02.10.2026): basis.notes enthält econ_lead_band. Der Punktpreis price_eur_mwh bleibt. Kill =0 stellt die bisherige Preisordnung wieder her. Die Bridge-Zielstunde zählt nur bis D+2, wenn Horizont-Tage mitgegeben sind.

Speicherplan v1.38 (Default an, alle Anlagen): decision_engine ist hours_demand_v1.38, solange eines der Flags an ist. Hold reserviert den Speicher für spätere Peaks, verschiebt Entnahme auf teurere Stunden (move ≠ add) und kann eine billige Nacht bis zum nächsten Morgen über die ganzen 168 h schonen, ohne neue kWh zu kaufen. Der tageübergreifende Kauf bleibt bis D+2. Eine Restladung vor jeder PV-Erholung (Decide und DA-Replan, nicht die Jahresrechnung) bleibt unter dem Morgen-Cap. Der Cap ist eine Decke, kein Auffüllen auf +10 Prozentpunkte. Die Stunde ist eigene Zeile (prefill_breach). Die Suche beginnt an der ersten Stunde des PV-Blocks, in der Nacht am Cursor, und wird nach dem Frühzug noch einmal auf denselben Speicherlauf wie die Anzeige gesetzt. Eine Schonstunde entlädt den Speicher nur bis zu ihrem Cap; eine Stunde, die schon lädt, nicht. Sie erzeugt keinen Floor. Die Stunde muss mindestens 5 €/MWh billiger sein als die Floor-Stunde, sonst entfällt der Nachzug. Weak-Rest und Lookback-Delta ziehen im selben Sonne-Fenster auf eine mindestens 5 €/MWh billigere Stunde vor der nächsten Morgensonne. In der Wochenrechnung wandert nur das Lookback-Delta, auf der vollen Ladekarte. Die Karte bleibt der Kalendertag. Fällt der Nachzug für diesen Floor aus, sucht der Lauf im selben Sonne-Fenster weiter. Lookback nimmt eine vom Soft-Cap blockierte Tagstunde, wenn sie mindestens 5 €/MWh unter der billigsten Nachtstunde liegt und keine PV verdrängt. Die Probe setzt nur den freien Anschluss an, entlädt eine Schonstunde nur bis zu ihrem Cap und stoppt am Ziel-SoC. JSON true/false ist kein Preis, keine kWh und kein PV-Block: die gemeinsame Preislesung, die PV-Erholung und die Sonnenfenster zählen es als fehlend. In der Decide-Preisreihe, im Weak-Fill und im Tagespreis füllt es nicht als 1 €/MWh vor. Neighborhood- und Thin-Cover-Carry sowie Lookback-Thin zählen es nicht als Last oder Ladeleistung. Cover-Defizit, Autark-Gate, Frühzug und der Entnahme-Ping-Pong zählen es nicht als Kapazität oder Ladeleistung. Hold-Pack und die ausgerichtete PV-Reihe zählen es nicht als kWh. Cold-Start-PV und der Rest-heute zählen es nicht als kWh. Die Nachtkarte zählt es nicht als Stunden oder kWh. Der Leergrund zählt es nicht als Ladeleistung. Das Cover-Fenster und die Horizont-Last zählen es nicht als kWh. Weak-DP und der Decide-Mix zählen es nicht als Kapazität, Ladeleistung oder Netzanschluss-Limit, und Cap-Skip behandelt den Speicher dann nicht als voll. Pflicht- und Prep-Stundenindizes zählen true nicht als Stunde 1, und die geladene Energiemenge zählt es nicht als 1 kWh. Explain, Vorrat, Session, Profil, Outlook und der Zähler zählen es nicht als Stunde, kWh, Anschluss, Jahresverbrauch, kWp oder Wirkungsgrad 100 %. Liegt der Floor schon auf der ersten Suchstunde, sucht der Lauf im selben Sonne-Fenster weiter. Anschluss voll oder Speicher auf Cap: die nächste billigere Stunde. Die Tages-Umlage streicht sie nicht und schiebt sie nicht hinter den Engpass. Negativpreis (unter 0, nur Band 0) ist unabhängig vom Opp-Schalter der Anlage. Der Tagesfuß zeigt Mindeststand-Netzbezug extra; die geplante Summe zählt ihn nicht mit. Nach dem Prefill-Reseat liegt die Pack-Tiefe des ersten Trogs eines PV-Tags, geteilt durch den Wirkungsgrad, auf der billigsten erreichbaren Stunde dieses Tags. Ein früherer PV-Tag öffnet sich nur, wenn der eigene den Mindeststand nicht trägt. Ein kurzer Rest am Horizontende bleibt ein eigener Block. Eine Stunde vor einem vollen Speicher deckt einen späteren Trog nicht. Lücken bleiben leer. Der freie Speicher zählt nach der Last der Stunde. Hält der Mindeststand, bleiben die Slot-Caps, solange sie die Netzrechnung nicht erhöhen; überzählige Ladung fällt weg. Eine Ladestunde entlädt den Speicher nicht gleichzeitig. Entnahme über Mitternacht reicht bis zum Ende des Folgetags, einmal pro Tag nach dem Tagesvergleich. Kill HOURS_CHEAP_REACH_DEFER=0. Kills: HOURS_HOLD_LATER_PEAK_RESERVE, HOURS_HOLD_DISCHARGE_PING_PONG, HOURS_HOLD_PRICE_PEAKS, HOURS_HOLD_MIDNIGHT_CARRY, HOURS_PREFILL_BREACH_TOPUP, HOURS_LOOKBACK_DAY_STAMP_PROBE, HOURS_OPP_NEGATIVE_PRICE, HOURS_COVER_EARLY_PLACE, HOURS_CHEAP_REACH_DEFER jeweils =0. Alle aus und Lead-Band an → hours_demand_v1.37. PicoC unverändert.


{
  "decision_status": "ok",
  "decision_engine": "hours_demand_v1.38",
  "prefix_hours_needed_today": 2,
  "prefix_period_hours_today": 10,
  "prefix_energy_charge_kwh": 4,
  "charge_now": 1,
  "charge_stop_soc_pct": 72,
  "target_soc_pct": 90,
  "capacity_kwh": 10,
  "hours_needed_today": 2,
  "hours_needed": 3,
  "period_hours_today": 10,
  "grid_charge_allowed": true,
  "charge_power_kw": 4.5,
  "discharge_power_kw": 9,
  "discharge_reason": "recharge_planned",
  "cheap_take_hours": 5,
  "hour_is_cheap": false,
  "cheap_hold": false,
  "min_soc_pct": 20,
  "adaptive_discharge": true,
  "energy_charge_kwh": 4,
  "hours_needed_days": [2, 1, 0, 0, 0, 0, 0],
  "next_charge_start": "2026-08-17T19:00:00+02:00",
  "next_charge_hours": 2,
  "charge_hours_today": [
    {
      "start": "2026-08-17T19:00:00+02:00",
      "end": "2026-08-17T20:00:00+02:00",
      "price_ct_kwh": 6.1,
      "energy_kwh": 2
    }
  ],
  "basis": {
    "cover_window_hours": 14,
    "cover_window_hours_pv": 14,
    "cover_extended_for_reserve": false,
    "cover_recovery_ok": true,
    "cover_dawn_cap_hours": 38,
    "energy_cover_dawn_kwh": 4.2
  }
}

Übersetzung: Lade 3 Stunden über den Horizont; 2 davon fallen ab jetzt heute fürs EMS an. Soll-Netzbezug 4 kWh (auf ganze kWh aufgerundet). Period 10 gilt, wenn der Decide um 13:00 kommt (Rest bis 23:00). Vergangene Stunden des Kalendertags zählen nicht.

API vs. App: Decide/EMS liefern Kalendertage — hours_needed_today, period_hours_today, prefix_*, day_plan, charge_hours_today gruppieren nach lokalem 00:00–24:00 (Period = Rest bis 23:00). Nur die Explain-Karte Folgetage in Portal/Sparvo rechnet PV-Tage (Morgensonne→Morgensonne, Feld rest_day_balances im Snapshot, nicht in der API-Response). Sichtbarkeit 1A: getrimmte Folgetage-Daten (REST_DAY_UI_MAX=7, Incomplete-Trim) gatekeepen PV-Nav und Wochenblick — ein Kalendertag erscheint überall oder nirgends. Sichtbare Menge = Folgetage-date ∪ Kalendertage aus [hour_lo, hour_hi) ∪ lokales Heute (D0 verschwindet nicht nach Morgensonne). Hold→Folgetage per Stundenfenster [hour_lo, hour_hi). Client (hx57): Prefs-Chips-Helper top-level; SSR-Folgetage erst ersetzen wenn ≥1 Tag gemalt — sonst SSR behalten / leere Schale ausblenden (kein Hint-only).

Die teuersten Stunden eines vollständig bepreisten Tages bekommen keine Netzladung in den Speicher. Fehlt eine Stunde oder liegt die Tagesspanne unter 5 €/MWh, gibt es keine Sperre. Zeigt 110 % der PV-Prognose in den nächsten 24 Stunden eine Einspeisung (das Wetter ist meist besser als die Prognose) und hält der Mindeststand auf der rohen Prognose ohne die Netzladung davor, entfällt sie. Die Ladung ab dieser Stunde bleibt. Fällt der Speicher sonst unter den Mindeststand, bleibt höchstens eine Nachladung: nur die fehlende Last dieser Stunde, begrenzt durch Ladeleistung, gestrichene kWh und den Platz, der bis zu dieser Stunde noch in den Speicher passt. Was eine volle Stunde davor nicht mehr aufnimmt, wird nicht gekauft. Es gilt die billigste Stunde, die den Fehlbetrag ganz deckt. Deckt keine Stunde ihn ganz, gilt der größte ankommende Anteil. Die laufende Stunde zählt einmal anteilig. Hat die Cover-Glättung sie schon gekürzt, bleibt dieser Rest. Entladung in teuren Stunden bleibt. Kill HOURS_PRICE_PEAK_CHARGE_BAN=0. Eine Stundensumme unter der Krümelgrenze, deren Anschluss größer ist, fällt nur weg, wenn die Floor-Stunden über alle Fenster bis zur achten Sonne mit und ohne sie dieselben sind. Eine Verschiebung des Floors lässt sie stehen. Ein kleinerer Anschluss bleibt. Kill HOURS_CHARGE_CRUMB_DROP=0. Netzladung, die spätere PV bei 110 % der Prognose aus dem Speicher drängen würde, wird nur um diese kWh gekürzt. Dieselbe Stunde verliert nur, was nach 110 % der PV nicht mehr hineinpasst. Der Mindeststand gilt auf der rohen Prognose. Die laufende Stunde zählt nur einmal anteilig; ist sie schon der Rest, bleibt dieser Rest. Kill HOURS_PV_EXPORT_HEADROOM=0.

Portal / Sparvo — Wochenblick

Einspeisung (EEG): In der PV-Zeile von Anlagenprognose und Sparvo Anlage steht, wie viele kWh an dem ganzen Kalendertag weder die Last noch der Speicher aufnehmen (export_feed_kwh, AC, 1 Nachkommastelle). Das ist die Tagesmenge — schon eingespeist plus noch prognostiziert — und sie aktualisiert sich im Lauf des Tages. Der Speicher-Chart rechnet heute weiter erst ab jetzt (der Live-SoC enthält die Vergangenheit schon). Die Einspeisung zählt abgeschlossene Stunden extra, anhand der SoC-Pings, und den Rest ab dem Live-SoC. Ohne Ping und mit Speicher werden fehlende Stunden nicht erfunden. Geplante Netzladung belegt den Speicher und senkt den noch kommenden Teil, und sie lässt Platz für 110 % der PV-Prognose, soweit der Mindeststand auf der rohen Prognose hält. Sparvo-Tageschips und Portal-Tagesbuttons zeigen die kWh immer, auch 0,0. Alle Anlagen/Keys. Steht nicht im Hours-JSON und nicht im Loxone-PV-Text.

Unter Anlagenprognose (Portal) und Sparvo Anlage zeigt der Aktuelle Wochenblick den 7-Tage-Plan aus derselben Shared Engine (day_plan, Default-Horizont 168 h). Keine Summen-Chips „Woche N h“ / „Heute (EMS)“ / „Nächste …“ und keine Layers-Zeile Cover/Suchhorizont — die Tagesleiste, Lead („System schaut … h voraus“) und der EMS-Hinweis reichen; bei heute=0 und Folgetag-Ladung erscheint die nächste Ladung nur in der Why-Zeile (why_later). Plan-Timeline: Explain zeigt zwei Folds (Netzladung + Bis morgen früh · Folgetage) mit chronologischer plan_timeline im Netzladung-Fold (keine separate „Diese Woche“-Prosa); der Wochenblick echo’t teaser plus höchstens 1–2 Tageszeilen — nicht die volle Uhr-Prosa aus basis.week_status.lines (API-Feld bleibt). Autark = kein Charge/Hold und keine Tages-Restlast; Idle mit Restlast ohne Hold → no_hold_gap; Hold ohne Netzimport = Speicherschonen (Entnahme Slot-Cap continuous hold_share oder ≈1 kW Weak/cheap_hold oder Speicherreserve 0…0,2, hold_hours_local) — nicht dasselbe wie Autark. Cover schlägt Interim-Pass-Hint. Jeder Decide rechnet neu über now, now+H≤168). Das EMS bekommt weiter nur die heutigen Stunden — der Wochenblick ist Transparenz, keine zusätzliche Steuerung. Die Kachel Speichergröße & Spot-Kosten (auch Gast-Demo Graz) nutzt dieselbe Vorrat-Policy v1.8 (Typwochen, Familie B: Cap + Port + Predicate) — kein zweiter Rechenkern. Horizon-Hold: Shared Builder mit Decide; Jahres-Sim = Hold-Entnahme min(net, Cap, s−floor) (Decide O12 = Slot-Cap). Hold nur bei Cover-Lücke. Die Explain-Anzeige „Hold nur bei ungedeckter Nacht“ ist UI-only und ändert die Jahres-€ der Kachel nicht; ohne Speicher kein Hold. Persönliche €-Kacheln bleiben noindex (/plant, /plant-outlook); öffentliche Auffindbarkeit über [Sparvo llms.txt und SpotpriceAPI Docs.

Der Fold Netzladung erklärt den Plan so: aktuelle Periode (noch X Stunden bis Morgensonne/Datum-Uhrzeit) · in dieser Zeit until-Prosa (until-none nur wenn der Kalendertag Heute wirklich autark ist — kein Laden/Hold/Restimport (inkl. charge_by_date-Slots); no_hold_gap versteckt until-none; kein Phantom-kWh ohne Charge-Segmente) · chronologische Timeline (plan_timeline: Speicherschonen-Ranges mit (Speicher: ~x kW, Netz: ~y kW, SOC: ~a %→~b %) = SoC Segmentbeginn→Periodenende, nur Ende Ende ~b %, unbekannt SOC: —; nicht der Live-Gauge — Decide rollt ab Telemetry-SoC, Overlay aktualisiert nur „Jetzt“) + Lade-Bullets mit Unique-Start-Merge/kWh/Reason/Stopp-SOC; Autark still) · Heading „Danach…“ nur für Events nach Periodenende. Die frühere Prosa-Kachel „Diese Woche“ entfällt in der UI (basis.week_status bleibt in der API; plan_timeline UI-only). Folgetage-Versorgung-Torte: PV · Speicher · Netz Haus · optional Netzladung→Speicher (Flow-Nenner). „Mindestladestand“ nur im ⓘ, wenn das Cover-Fenster über die erste Morgensonne hinaus gestreckt ist. Sonderfälle ohne Cover-Plan (keine Ladung / Netz aus / Dump / Bridge) behalten Chip+Kurztext — innerhalb des Folds Bis morgen früh · Folgetage (Nachtkarte; nicht über dem Netzladung-Fold). Speicherschonen-Chip nur wenn Nachtlast nicht durch Speicher+PV gedeckt ist; Folgetag-Holds allein zeigen keinen Hold-Banner. „Keine Netzladung nötig“ nur wenn der ganze 7-Tage-Plan leer ist. Unter Bis morgen früh / Bis heute früh bleibt die Nacht-Bilanz (Last/PV/Speicher, Ohne·Mit Netz) mit Stempel um HH:MM bei reiner Uhrzeit bzw. am {Datum Uhrzeit} (erste Morgensonne). Ohne Netz in der Anzeige = kein zusätzliches Laden in den Speicher; die Notstrom-Reserve (min_soc_pct) bleibt in der Simulation geschützt — darunter entlädt der Speicher nicht weiter, fehlender Verbrauch läuft direkt aus dem Netz. Folgetage listet PV-Tage (Morgensonne → nächste Morgensonne) bis Prognoseende. Zwischen 00:00 und Morgensonne: die Nacht nur unter Bis morgen früh; erster Folgetag = heutiger PV-Tag ab Morgensonne (nicht die Nacht nochmals). Nach Morgensonne / vor Mitternacht: Folgetage ab der nächsten Morgensonne. Aufklappbar: Balken Last/PV und Ohne·Mit Netz über dasselbe Sonne→Sonne-Fenster; Verfügbarer Speicher · jetzt (Nachtkarte = Live-Stand) bzw. · um HH:MM (Folgetag = Stand am PV-Tagesbeginn / Morgensonne) — Füllgrad der nutzbaren Bandbreite (Kapazität − Mindeststand), nicht „voll“ nur weil kWh ≫ Nachtlast. Netzladung erlauben im Anlagenprofil steuert den Balken: an → kWh inkl. geplanter Netzladung bis zur Morgensonne (erwartete Trajektorie); aus → ohne Netzladung. Gilt für alle Anlagen/Keys. Bei geplanter Ladung am Kalendertag der Kachel zusätzlich Geplante Netzladung … · Effekt zur Morgensonne: +X % (nur Stunden, die nicht als übersprungen gelistet sind; Nachtstunden des Folgetags zählen nicht als Sonntags-Plan). Ohne·Mit-Netz am Morgensonne-Stempel bleibt die echte Trajektorie. ⓘ neben dem Morgensonne-Stempel erklärt Ohne/Mit und Reserve. Letzter Tag ggf. Prognoseende wenn der 168h-Horizont vor der Erholung endet. Mit Netz bleibt sichtbar, ausgegraut wenn gleich wie Ohne. Der frühere Mehr-Details-Technikblock entfällt. Wochenblick: hours_needed_days wird auf die sieben Kalendertage (D0…D6) gemappt; pro Tag N h geplant bzw. kWh geplant — nicht „Keine Ladung“ bei sichtbarem Balken.

Lastprofil aus dem Zähler (kurz)

Wenn Sie regelmäßig Plant Telemetry schicken:

1. Nach ≥1 vollem Tag Historie: geschätzter Tagesverbrauch aus recency-gewichteten Tagessummen (jüngere Tage stärker), WD/WE getrennt wenn möglich. Unvollständiges Heute zählt nie als ganzer Tag (auch nicht für D+1). 2. Heute: die Schätzung mischt Typical mit dem bisherigen Zählerstand über die WD/WE-Stundenform (nicht Uhrzeit × 24). Ein heißer Vormittag darf Rest-heute nicht auf 0 setzen, nur weil Ist schon über dem Typical liegt. Fehlende Stunden-Samples (None) zählen nicht als 0 kWh: dünne Tage (&lt;12 Stunden) fließen nicht in die Form ein; nie gesehene Stunden bekommen eine Flat-Prior — sonst wäre die Cover-Nachtlast 0 und der Planende-SoC unrealistisch hoch. 3. Einmalige Spitzen (z. B. E-Auto) werden weich gekappt (~2× Median), damit die Netzstunden-Planung nicht eine Woche überreagiert. 4. Bis genug Historie da ist: Fallback Jahresverbrauch ÷ 365 (Portal-Hinweis). Sobald genug vollständige Zählertage seit Aktivierung da sind, schreiben wir Mittelwert dieser Tage × 365 (auf 100 kWh aufgerundet) ins Anlagenprofil — Portal, Sparvo, Telemetry und Hours persistieren denselben Wert (nicht nur anzeigen). Recency-Typical bleibt für Tages-/Rest-heute-Last. 5. Cover deckt die Lücke bis zur nächsten guten PV (basis.cover_window_hours) — nicht die volle Wochenlast. v1.13: Dawn-Cap clippt Cover/Bridge an hi(next_pv) auch bei Stretch (Port = Ladeleistung−PV) — jeder Wochentag im ≤168 h-Horizont, nicht nur WE; Soft-Cap schichtet Overnight + Recovery-Tagesstempel + Dawn (min der Rooms — Dawn ersetzt Near Soft-Cap nicht); kein Cover hinter dem Dawn-Stamp (Folgetage → Vorrat); energy_deficit_cover_kwh folgt dem Dawn-geclippten Budget. v1.12: hält der nächste PV-Tag den Chrono-Trog mit Margin (basis.cover_recovery_ok), entfallen Stretch und D+1-Union; Soft-Cap-Rest nach Near wird nicht als Far/D+1-Cover oder Bridge nachgelegt außer Soft-Cap-Abandon-Spill (HOURS_SOFTCAP_ABANDON_SPILL oder Darf-S1 HOURS_DARF_SOFTCAP_S1, Gate OR, default on; Kill =0) in das nächste Soft-Cap-Fenster; Rebalance/Consolidate verschieben Cover nicht hinter Near. v1.9: Kurzfrist-Cover (heute bis Morgensonne) verfeinert die Stundenlast mit Teilstunde und Recent-Rate (letzte 2–4 Meter-Stunden); Metadaten basis.load_refine. Folgetage bleiben Typical-Profil, außer drei gleiche Zählerstunden heben oder senken dieselben Uhrzeiten der nächsten 24 Stunden (Deckel nur nach oben, das Doppelte der Typical-Stunde; eine einzelne Spitze oder Senkung zählt nicht; ein fallender Speicher ist keine ruhige Stunde; die laufende Stunde zählt nur anteilig, wenn die Cover-Glättung sie schon gekürzt hat). Cover-Slot-Rang (alle Kalendertage gleich): pro Tag Preis-Rang (1./2./3. günstigste ladebare Stunde), global die günstigsten Tagesköpfe bis Bedarf gedeckt — Uhrzeit blockiert nicht (z. B. 12:00 und 13:00 vor 16:00 wenn billiger und machbar). Frühere Mittelpreis-Stunden entfallen nicht, nur weil eine spätere noch günstigere Cover-Stunde schon voll ist — sonst würde Restbedarf in teurere Stunden rutschen. Machbarkeit: SOC-Headroom, Port-Raum charge_power_kw − PV, Skip pv_fills_charge_power nur bei Port≈0 / vollem Speicher (Partial Port 0 < Port ≤ min_headroom bleibt ladebar). Port-Raum je Stunde = charge_power_kw − PV aus dem Anlagenprofil (nicht fest 8 kW). Client-load.hours[] bypassed Refine. Vor Sonnenaufgang zählt ein 3-h-Lookahead nicht als „schon in PV“: das Fenster endet an der heutigen ersten guten Stunde (cover_window_hours_pv), nicht an morgen 07:00. Zwischenladen (Interim) sucht die nächste Billigstunde nur in 24 h — nicht den billigsten Slot der Woche — und nicht in einer Stunde, deren PV schon die volle Ladeleistung belegt. Cover, Dump und Prep planen in einer PV-Stunde nur den Rest Ladeleistung − PV (1 h: kWh≡kW; das EMS zieht nur diese Differenz vom Netz) — auch bei Mehrfach-Zuweisung derselben Stunde (bereits geplante Netz-kWh zählen mit, kein Fallback auf Speicher-Headroom). Skip pv_fills_charge_power nur wenn Port≈0 (Partial Port bleibt ladebar). Nach dem Picker: chronologischer Clip — keine weitere Netzladung, sobald Stopp-SOC 100 % / Speicher voll. Stunden-Roll Last/PV zuerst, dann Netz: ein voller Speicher zu Stundenbeginn sperrt nicht eine Netto-Entlade-Stunde (sonst entfallen günstigere Stunden wie 15:00 zugunsten teurerer Top-ups 16/17). Same-Day-Rebalance nach dem Clip bevorzugt günstigere Stunden (bidirektional, Accept-if-better). Reicht die erste Morgensonne nicht, um den Mindest-SoC über die folgenden Nächte zu halten (typisch schwaches Wochenende) und ist Recovery false, wird das Fenster bis zur nächsten echten Erholung verlängert (cover_extended_for_reserve), aber nicht in den unvollständigen Nacht-Stub am Horizontende. Near vs. Far (v1.5): Survival und Stunden bis zur ersten Morgensonne laden nur den Near-Bedarf (basis.energy_cover_near_kwh, Soft-Cap Morgen-SOC ≤ min+10 pp); Stretch-kWh erst danach — kein Front-Load der Wochenend-Reserve als volle Ladeleistung heute. v1.8: Prep sucht rückwärts vom Bedarfstag in der Trageweite \(H_{eff}\) (basis.carry_hours); Lookback nur vorrat_wuerdig (P20 ∧ Absolutschutz); Cap unter Ziel-SoC; Dump-Sizing vor Vorrat (e_opp = fill − cover); Placement Near-Cover → Soft-Cap-Abandon-Spill (Flag/Darf-S1) → Prep → Far-Cover → Bridge → Opp; Quelltag ohne Netz schon voll durch PV → keine Prep-kWh an diesem Vortag (auch nicht nachts). Far-Cover darf dieselben Fill-Stunden nicht als Hintertür nutzen (nur [Trog−H_{eff}, Trog]). Prep vor jeder Morgensonne (nicht nur der ersten Nacht) darf den Morgen-SOC nicht über min+10 pp heben; Vorrat füllt nur bis zum Prep-Floor, nicht bis Ziel-SoC/100 %. Prep-PV-Tage sind Morgensonne→Morgensonne (kurze Mittagsdips sind keine Tagesgrenze). Ein am Horizont abgeschnittener letzter PV-Tag ohne nächste Morgensonne bekommt keine \(L_D\); Vortag-Stunden nur bei endlichem \(P_{ref}(D)\). Opportunistisches Nachladen (wenn aktiv) nur unter dem Grenzpreis im Suchhorizont. Bei Dump aus und gedecktem Cover: keine Netzladestunden (auch keine Bridge). v1.3: Ladezeiten nur, wenn Speicher Platz hat — voller Speicher + PV zur gleichen Stunde → Slot in skipped_slots, Ladung verschoben.

Typischer Ablauf mit Batterie


Oft: Telemetry (SOC + Zähler)
Selten: Decide (neuen Plan holen)
EMS: Spot-Optimierer mit hours_needed_today

In Loxone Schritt für Schritt: Loxone Hours & Telemetrie.

Integration EMS (Prefix vs. kanonisch)

Die Decide-JSON enthält zwei Sichten auf den heutigen Ladeplan:

BedarfPrefix (Spot-Optimierer-View)Kanonisch (voller Plan)
Demand heuteprefix_hours_needed_todayhours_needed_today
Periodprefix_period_hours_todayperiod_hours_today
kWh-Sollprefix_energy_charge_kwhenergy_charge_kwh
Slotsprefix_charge_hours_today (kompakt)charge_hours_today (voll)
Jetzt Ladestunde?charge_now oder Slot-MatchSlot-Match auf charge_hours_today

Extract v1.23 (O9-Headroom bis Session-Stopp/O4; Latch → Rest=0): O1/O2/O9 aus Prefix-Feldern. O3 = gated Relais (charge_now), O4 = charge_stop_soc_pct (Prefix-Ende), O5 = PulseSessionStop → Fetch I5+I6, O1 = 0 wenn O9 = 0. EMS-Prefix trägt additiv charge_latched / charge_stop_hysteresis_pp (SoT Server); Extract liest die Hysterese-pp für lokales Latch-Band. Nach 12 h ohne API: lokaler Fallback. Fetch v1.17 (SOC-5%-Stufen; kein Smart-15-Decide).

Optimierer vs. API-Slots

API plant konkrete Slots; der Spot-Optimierer mit Demand/Period wählt günstigste N Stunden — kann abweichen. Relais nur von Extract O3. O1 wird 0 sobald O9=0.

Nicht-kontiguer Plan: prefix_hours_needed_today kann kleiner sein als hours_needed_today (Insel-Slots).

Day-Ahead-Replan (EMS / Webhook)

Sobald Day-Ahead vollständig vorliegt und die Inferenz mit price_source=day_ahead durch ist (status=received und received_for = morgen), replant Spotforecast sofort alle aktiven Anlagen (mode=decide, Snapshot + optional Webhook plant.plan_updated). Der Poll läuft stündlich ab ~13:00 Wien; Catch-up bis ~23:00. Solange der Poll noch waiting/skipped ist, bleibt der Plan auf dem letzten Decide — D+1 im Preise-Chart bleibt forecast_d1, bis received_for gesetzt ist.

Zusätzlich: ein SOC-Ping nahe am Mindest-SoC (10 pp / 5 pp / Floor) oder beim Wiederanstieg oder wenn eine Ladesession am Stopp-SoC fertig ist (Session-Complete: nur bei Snapshot-charge_now==1, Debounce 120 s nach Decide; Stopp/charge_now fail-closed via coerce_charge_stop_soc_pct/coerce_charge_now, JSON-bool → idle) oder bei Hold-Last-Drift (Now ∈ hold_hours, Live-Import vs Cap-erwarteter Import max(0, plan_rate−discharge_cap_kw), Debounce 120 s; Kill HOURS_HOLD_LOAD_REPLAN) löst denselben internen Decide async aus (Snapshot + PV-Tagespack + Webhook). Sparvo/Portal zeigen frischen Snapshot (Prefer ≤900 s) mit Live-SoC-Overlay; Loxone-Extract folgt beim nächsten Hours-Decide (:00 / SOC-Stufe / I6) bzw. Webhook-Trigger.

ThemaVerhalten
Webhook-PayloadKompakt: charge_hours_today + pv_forecast_url / pv_hash — voller Plan bzw. PV per GET pullen
PV-Tagespack SoTTagespack in DB = Modell-FC (last_pv_forecast_*, pv_hash). Serve GET/loxone und Portal/Sparvo: D0 = Calendar-Meter-Blend (Ist+Prognose) keyed nach Kalenderdatum wenn PV-Zähler-Samples da sind; sonst FC. Tagespack nicht fresh bei leeren Hours / Kalender-Roll; Serve pad/strip/synthetic D0. Folgetage = FC
plan_hashOptional im Event — Dedupe gegen letzten Pull
MaterialitätPush auch bei nur morgen geänderten Slots oder PV-Tagespack-Änderung
Portal ExplainFrischer Snapshot Prefer; Live-Resolve nur wenn Snapshot fehlt/stale oder Profil grid_charge_allowed=false bei noch geladenem Snap (Demand/charge_now/prefix_*/charge-plan_hour_roles/energy_charge_kwh/nested day_plan) — dann Live-Decide + publish_hours_and_pv_after_decide (kein Snapshot-First-Short-Circuit). Overlay hide Now bei SoC≥Stopp (Chips/charge_days_*). Profil-Speichern löst sofort Shared Decide aus; incomplete/Fehler → Tombstone (profile_refresh, hours_unavailable, leere Charges, discharge_power_kw=0/charge_now=0) + Webhook; Hydrate über Tombstone publisht inkl. Webhook (kein silent Persist); load_raw_snapshot refreshed Plant-Row (kein Identity-Map-Stale); Hydrate skippt silent Persist bei fortgeschrittenem issued_at; wenn auch Tombstone-Schreiben scheitert → Flash plan=error_stale. Decide-OK ohne persistierten Snap → Flash error_stale. Netzladung an→aus ohne plant.plan_updated → Flash webhook_not_configured / webhook_failed. Live-Telem-Strip erzwingt charge_now=0 bei Profil-aus und bei Cold-Start-Pause (hours_control_paused). Why-UI display-only: bei Profil-aus/Pause keine stale Demand/LoadNow-Narration (inkl. charge roles/week_status); empty_reason: Pause → cold_start (auch Pause∩Grid-off); nur Grid-off ohne Pause priorisiert grid_off vor covered/hold. Leeres day_plan bei frischem Non-Tombstone-Snapshot ist gültig (Autark/Covered) — kein erzwungenes Live-Decide nur wegen leerer Liste
IdempotenzFolge-Timer am selben DA-Tag überspringen Anlagen mit bereits aktuellem Decide (already_current) — das ist kein incomplete_input. Tombstone nach Profil-Save erzwingt Catch-up (profile_refresh_tombstone); bleibt der Tombstone nach einem nicht-ok Decide kleben, redispatched DA den leeren EMS-Clear (nicht nur bei incomplete_input). Grid-off + noch geladener Snap nach nicht-ok Decide → neuer Tombstone + ein Webhook
UnvollständigNur wenn Cap/SOC fehlen → API/incomplete_input bzw. Batch-Skip bis Profil/Telemetrie nachzieht; Profil-Save räumt trotzdem alte Charges im Snapshot (außer error_stale)
Fill-Day / VorratTag, der ohne Netz den Speicher füllt und Cap-Need hat → kein Prep-Gap (fill_day_not_gap). Netzladung, die den späteren Hausbezug nicht senkt, fällt weg. Eine schädliche Stunde löscht eine nützliche nicht. Speicher-kWh auf einer Stunde, deren Netz den Entladeraum am Beginn übersteigt, liegen auf früheren billigeren Stunden, die den Mindeststand noch erreichen. Der Kilowattstunden-Clip lässt sie dafür stehen (HOURS_CHARGE_SURVIVES_USE)

Häufige Anfängerfehler

ProblemLösung
incomplete_inputProfil im Portal füllen oder Cap/Pmax/SOC mitschicken
Basic-KeyCommunity registrieren
Plan blind verkleinern mitten am TagExtract v1.23 (O9-Headroom bis Session-Stopp/O4; Latch → Rest=0): O1=0 wenn O9=0 — Relais nur über O3
horizon_hours als Optimierer-PeriodPeriod ist Rest bis 23:00 (23:00 → 24); Extract v1.23 mappt prefix_period_hours_today
Vergangene Stunden als Demand heuteSlots gelten ab jetzt; hours_needed_today=0 heißt: heute nichts mehr
kW × Stunden als kWhenergy_charge_kwh ist die aufgerundete Sollmenge; Extract O9 (Rest-kWh)

Nächste Schritte

Mit SpotpriceAPI starten

Community registrieren oder Code-Beispiele mit Ihrem persönlichen Key.

Live API-Status