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.17. Offene API für HA/Python; Community 120 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; optional Dump vor weiterem Vorrat — 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: O11 / charge_power_kw = Anschluss × 0,80 − Grundlast (Grundlast = Jahresverbrauch/8760, falls kein explizites Feld). Beispiel: 12 kW Anschluss + ~17 900 kWh/Jahr → ≈ 7,6 kW (nicht 12). Thin-Decide ohne battery.max_charge_kw im Body, sonst capped der Request die Leistung.
  • 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 Speicherleiter 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: 5Maximal 5 kW aus dem Netz laden
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 (nach Decide)
charge_stop_soc_pctStopp-SOC dieser Netzladung (%) — Extract O4. Survival/Mindestladestand: Reserve-Floor (min_soc_pct) nur wenn der aktuelle SOC darunter liegt; sonst Trajektorie/Ziel (nie Stopp unter Live-SOC)
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 wirtschaftliches Zwischenladen vor der nächsten Billigstunde nötig ist. 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
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.17: O5–O8 frei (Portal/Wochenblick). UI Wochenblick: Index = Kalendertag D0…D6 — nicht der Index in der sparse day_plan-Liste.
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)
discharge_power_kwEntlade-Cap in kW — Extract O12; fehlend → PicoC-Fallback 9. Cheap-Hold nur wenn PV+Speicher bis zur nächsten Morgensonne nicht reichen: aktuelle Stunde unter den günstigsten N und nicht am Laden → 0 kW. Nicht bei vollem Speicher (≥99 %) und nicht in einer Stunde mit PV-Überschuss. Stretch/Reserve-Lücke allein reicht nicht. Bedarf bis PV gedeckt → normale Entnahme. Während charge_now volle Cap. Adaptive sonst: volle Cap, außer nahe Min-SoC und kein Nachladen. 0 auch an/unter Min-SoC ohne Nachladen oder bei User-Cap 0
min_soc_pctMindest-Speicherstand (%) — Extract O13
adaptive_dischargetrue = Reserve-Schutz nahe Min-SoC wenn Nachladen sich nicht lohnt; false = festes max_discharge_kw (Portal Standort / Sparvo Speicherregeln). Cheap-Hold gilt nur bei Cover-Lücke
discharge_reasonWarum O12 so steht: cheap_hold · 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. Cheap-Hold nur bei Cover-Lücke
hour_is_cheapaktuelle lokale Stunde gehört zu diesen N
cheap_holdtrue wenn O12 wegen Cheap-Hold 0 ist (Cover-Lücke + günstige Stunde + nicht am Laden)
hours_needed_cover / hours_needed_prep / hours_needed_opp / hours_needed_bridgeAufschlüsselung: Cover · Dump (Opp) · Vorrat (Prep) · Bridge — Picker-Placement Cover→Bridge→Dump→Prep (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). Response-Shape unverändert, kein neues Pflichtfeld, discharge_reason / O12 unverändert
plan_feasibilityok / partial / no_feasible_slots — geplante vs. untergebrachte kWh; storage_undersized
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
charge_hours[].soc_projected_* / slot_role / survivalErklärung pro Slot (Cover/Prep/Opp, SOC vor/nach). Portal/Sparvo: günstige Cover-Stunde · Vorratsstunde · Erhaltung Mindestladestand · günstiges Netzladen · Übergangsladung
day_planPro Kalendertag: N + Starts (Portal/Sparvo)
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
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 geplante Netzladung (Cover/Prep/Dump/Bridge = 0). Ausnahme: cover_extended_for_reserve → Stretch-Cover (stretch_reserve_outside_gate). autark_notes: gate_h:… oder Trog-Hinweis. Sparvo-PV-Kachel = voller PV-Tag; Engine = stündliche Rest-Serie. 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
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-€, Leiter, Sommer/Winter-Tage und savings_vs_* sind null — die stehen in der Anlagenprognose.
basis.charge_defer_enabledtrue = Mehrtage-Arbitrage (global günstige Stunden über Cover); große Packs → höherer effektiver Horizont
missingWelche Angaben fehlen (bei incomplete)

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 %). 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.


{
  "decision_status": "ok",
  "decision_engine": "hours_demand_v1.11",
  "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
    }
  ]
}

Ü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 Kalendertagehours_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).

Portal / Sparvo — Wochenblick

Unter Anlagenprognose (Portal) und Sparvo Anlage zeigt der Wochenblick Hours den 7-Tage-Plan aus derselben Shared Engine (day_plan, Default-Horizont 168 h). 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. Persönliche €-Kacheln bleiben noindex (/plant, /plant-outlook); öffentliche Auffindbarkeit über Sparvo llms.txt und SpotpriceAPI Docs.

Die Kachel Netzladung erklärt den Plan so: aktuelle Periode (noch X Stunden bis Morgensonne/Datum-Uhrzeit) · in dieser Zeit geplante Netzladestunden (inkl. kWh) · danach Liste der Folgeladungen (Datum · Tages-kWh · Startzeiten mit kWh und Stopp-SOC dieser Ladung je Slot aus soc_projected_end_pct; EMS-O4 überschreibt den aktuellen Prefix-Slot). „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. „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. 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 (<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 Typical da ist, schreiben wir Typical × 365 (auf 100 kWh aufgerundet) ins Anlagenprofil — Portal, Sparvo, Telemetry und Hours Persistieren denselben Wert (nicht nur anzeigen). 5. Cover deckt die Lücke bis zur nächsten guten PV (basis.cover_window_hours) — nicht die volle Wochenlast. 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. 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 vor 14:00 wenn billiger und machbar). Liegt schon eine spätere günstigere Cover-Stunde am selben Kalendertag, entfällt eine frühere teurere nur, wenn dazwischen keine offene Stunde liegt (z. B. kein Sa 08:00 direkt vor 09:00; Di 12:00 vor 14:00 bleibt, weil 13:00 offen). Machbarkeit: SOC-Headroom, Port-Raum charge_power_kw − PV, Skip bei pv_fills_charge_power / vollem Speicher. 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 Pack-Headroom). Skip, wenn die PV die Ladeleistung voll nutzt (pv_fills_charge_power). Nach dem Picker: chronologischer Clip — keine weitere Netzladung, sobald Stopp-SOC 100 % / Pack voll. Reicht die erste Morgensonne nicht, um den Mindest-SoC über die folgenden Nächte zu halten (typisch schwaches Wochenende), 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 vor Vorrat (Sizing + Placement); 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.17: O1/O2/O9 aus Prefix-Feldern (kein loxone_*). O3 = Relais (charge_now), O4 = charge_stop_soc_pct, O1 = 0 wenn O9 = 0. Txt3 = X Std, Y kWh. Nach 12 h ohne API: lokaler Fallback.

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)

Nach Day-Ahead + Inferenz (~13–16 Uhr) replant Spotforecast alle aktiven Anlagen intern neu (mode=decide, Snapshot + optional Webhook plant.plan_updated).

Zusätzlich: ein SOC-Ping nahe am Mindest-SoC (10 pp / 5 pp / Floor) oder beim Wiederanstieg (spiegelbildlich bis Headroom) löst denselben internen Decide async aus (Snapshot + Webhook). Sparvo/Portal rechnen schon live; Loxone-Extract folgt beim nächsten Hours-Decide (:00 / SOC-Stufe / I6).

ThemaVerhalten
Webhook-PayloadKompakt: nur charge_hours_today — voller Plan via GET /v1/decision/hours-demand pullen
plan_hashOptional im Event — Dedupe gegen letzten Pull
MaterialitätPush auch bei nur morgen geänderten Slots (voller Plan zählt)
Portal ExplainLive-Resolve — kann vom Snapshot abweichen bis zum nächsten Seitenaufruf

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.17: O1=0 wenn O9=0 — Relais nur über O3
horizon_hours als Optimierer-PeriodPeriod ist Rest bis 23:00 (23:00 → 24); Extract v1.17 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