Files
ems/docs/04-modules/planning-neg-sell-strategy.md
Dusan Vojacek 1429d402e5
Some checks failed
CI and deploy / migration-check (push) Failing after 13s
CI and deploy / deploy (push) Has been skipped
zdokumentovani noveho pohleud na planovani nabijeni
2026-06-01 19:53:04 +02:00

24 KiB
Raw Permalink Blame History

Strategie záporného výkupu, FVE A/B, termika a flexibilní zátěže (home-01)

Navazuje na planning.md, planning-arbitrage-accounting.md, planning-charge-slot-budget.md (plánovaná náhrada pre-neg cushion), planning-changelog.md, heat-pump.md, ev-charging.md.

Stav: část je implementovaná (v32v40), část je návrh (termika, bazén, spirála; charge-slot-budget — viz níže). V textu je označeno ✅ hotovo vs 📋 návrh.


1. Cíl produktu (home-01)

Cíl Popis
Baterie v okně sell < 0 Dojet na 100 % (soc_max) do konce denního úseku záporného výkupu (Europe/Prague).
Pole B (zelený bonus) Při záporném výkupu smí jít přebytek do sítě (ekonomika bonusu); není curtailable.
Pole A (Deye, curtailable) Po dosažení plánované energetické pohody nechat dostupné pro dům a chybu forecastu — ne nutně „vždy škrtat v 80 %“.
Ranní kladný sell Typicky export celé FVE do site — nekrást výkon TČ ani fiktivním nabíjením v plánu.
Termika TUV komfort / předehřát / večerní doklep — uvnitř vhodných oken, ne v ranním exportním pásmu.
Flexibilní sink Bazén (filtrace), později spirála — sežrat plánovaný přebytek místo exportu za záporný sell.
EV Odpoledne; nabíjení po naplnění energetické rampy / v levných slotech.

2. Slovník

Pojem Význam
Okno sell < 0 Souvislé 15min sloty téhož kalendářního dne (Prague), kde effective_sell_price < 0.
Tail Posledních N slotů okna (planner_neg_sell_full_soc_tail_slots, default 4 = 1 h). Cíl SoC = soc_max (100 %).
Prep (v32) Všechny sell < 0 sloty před tail. Dnes: plochý cíl planner_neg_sell_prep_soc_percent (default 80 %).
Bod T (t_detach) 📋 První slot (od tail zpět), od kdy forecast pole B (po loadu, s limitem nabíjení) sám dožene zbytek SoC na 100 %. Nahrazuje fixních 80 %.
E_surplus_after_t 📋 Integrál plánovaného přebytku FVE (typ. od T do last_sell<0), který by jinak šel do sítě / curtail — budget pro TČ předehřát, bazén, spirálu.
Pre-neg export (v33) Kladné sell před prvním sell < 0: export FVE jen pokud forecast v celém sell < 0 okně pokryje dobítí na prep cíl (× margin 1,15). 📋 Plánovaná náhrada: pre_window_wh v planning-charge-slot-budget.md §6.
Load-first (v34) Dům z pv_ld; při dostatečné FVE žádný fiktivní grid_import = load v plánu.
Rampa B + bod T (v35) soc_need zpět od tail jen z PV B; t_detach; E_surplus_after_t; uvolnění A po T (měkké).
Reg 340 Deye max solar powerpv_a_forecast_solver_w pv_a_curtailed_w.

3. Časová osa dne (referenční home-01)

        Prague
  |-----|-----|-----|-----|-----|-----|-----|-----|
  06    08    10    12    14    16    18    20

  [ A: ranní sell ≥ 0 — export FVE (v33)     ]
  [ B: sell < 0 — nabíjení bat, T*, TČ, bazén ]
  [ C: večerní peak sell — export bat (masky) ]
  [ D: EV často odpoledne / večer            ]

3.1 Fáze A — před prvním sell < 0 (ranní export)

  • Chování plánu (v33): pokud _pre_neg_pv_export_forecast_cushion_ok, sloty v pre_neg_pv_export_ts tlačí export (ge_pv), bc_pv = 0 (FVE ne do baterie).
  • Termika (📋): neplánovat komfortní TČ/TUV v těchto slotech — výkon by kolidoval s exportní strategií (FVE má jít do site).
  • Deye: load-first na zařízení — dům si vezme z FVE; plán ale může ukazovat export, ne „import pro load“ (viz v34).

3.2 Fáze B — okno sell < 0

Energie (v32v35):

Období v B Chování (v35)
Začátek okna Nabít podle rampy SoC (soc_need) zpět z PV B od tail
Střed okna Od t_detach: měkké omezení bc_pv; hold/curtail při soc_prev ≥ soc_target[t]
Tail (posledních N slotů) Rampa z soc_need[tail_start] → 100 %

Termika (📋):

  • primárně zde, když je dost PV / po bodu T, ne v ranní fázi A.
  • Předehřát v T jen pokud je T dostatečně brzy a E_surplus_after_t je velké.
  • Večerní doklep TUV (12 h před sprchou) — samostatné pravidlo od tuv_usage_stats.

Bazén (📋):

  • Jen slunečné hodiny v rámci B (a ideálně po T), X hodin/den — promíchání prohřáté hladiny.

3.3 Den bez sell < 0 (📋)

  • Přebytek FVE → prodej za kladný sell (ne „výmět“).
  • TČ: topit v slotech, kde COP × sell dává smysl oproti prodeji kWh (viz fn_cop_estimate, fn_heat_pump_cost_per_kwh_heat).
  • Spirála: spíš nízká priorita — každá kWh do spirály je kWh, kterou šlo prodat.
  • Bazén: volitelně v nejlepších PV slotech, pokud export není ekonomicky nutný.

4. Implementované vrstvy (v32v35)

4.1 v32 — fázované SoC a curtail A

DB: ems.asset_battery — migrace V083__planner_neg_sell_phases.sql

Sloupec Default Význam
planner_neg_sell_prep_soc_percent 80 v32 legacy — od v35 se v LP neřídí (rampa z B). 100 = vypnutí fází (_neg_sell_phases_enabled).
planner_neg_sell_full_soc_tail_slots 4 Počet 15min slotů tail před koncem denního sell < 0. 0 = bez tail.
planner_neg_sell_vent_min_sell_czk_kwh 1 (home-01) V tail: ventil pole B (ge_pv) pokud sell ≥ práh. NULL = jen při plné baterii.

Kód: backend/services/planning_engine.py

  • _neg_sell_day_phases()prep / tail / none per slot
  • prep_soc_shortfall, prep_hold_met_binary, měkké prep_hold_curtail / prep_hold_bcpv
  • Výstup: planning_interval.pv_a_curtailed_w, solver_params.masks[].neg_sell_phase

Omezení v32 (důvod návrhu v35):

  • 80 % není odvozené z délky okna ani z forecastu B.
  • Curtail A je měkký (penalizace ~1 Kč/kWh) — LP může v sousedním slotu znovu nabíjet.
  • Hold: soc_prev ≥ 80 % na začátku slotu, ne dynamická rampa.

Ověření: NegSellSocPhaseTests, MCP planning_interval + solver_params->'masks'.

4.2 v33 — export FVE před sell < 0 s forecast pojistkou

Kód: _pre_neg_pv_export_forecast_cushion_ok, _neg_sell_day_pv_usable_wh, pre_neg_pv_export_ts.

  • Export v kladných slotech před prvním sell < 0 jen pokud usable FVE v celém sell < 0 dni ≥ potřebné Wh na prep (× 1,15).
  • Jinak LP raději nabíjí z FVE (déšť / slabý forecast v okně).

Ověření: PreNegPvExportForecastTests, solver_params.inputs.pre_neg_pv_export_forecast_ok.

4.2b 📋 Plánováno — pre-neg jako energetický rozpočet (charge-slot-budget)

Stav: neimplementováno (specifikace 2026-06).

Problém v33 při zimě / krátkém okně sell < 0: binární cushion často projde (optimistický forecast v okně × 1,15) → ranní export FVE i při sell ~23 Kč, přestože uvnitř okna energie nestačí na rampu / 100 % tail — velká baterie (home-01) pak přijde do neg okna podnabitá.

Záměr (souhrn):

charge_target_at_neg  := soc_need[first_neg]  (rampa v35/v36, observed SoC)
in_window_wh          := sum forecast PV (A+B) v sell<0 sloty dne × η
pre_window_wh         := max(0, charge_target_at_neg  in_window_wh × reliability)

Před first_neg: allow_charge v nejlevnějších slotech (buy ASC) + PV surplus,
                dokud cum_wh < pre_window_wh
Export pre-neg:   jen sloty s PV přebytkem, které NEJSOU v charge frontě

Vazby:

  • Rampa / tail / T / curtail A — beze změny v LP.
  • v44 (neg_day_no_grid_before_neg_sell): plánované změkčení — grid před oknem povolen v N nejlevnějších buy slotech, pokud pre_window_wh výrazně převyšuje in_window_wh.
  • v36 per-den bundle zůstává; pre_window_wh se počítá per pražský den, ne globálně.

Detail: planning-charge-slot-budget.md §4§6, changelog Plánováno.

4.3 v34 — tvrdý load-first

Tag: 2026-05-28-load-first-hard-v34

  • gi ≤ bc_gi + max(0, max_load pv_forecast) — při vysoké FVE žádný fiktivní import = load.
  • Při pv_forecast ≥ max_load + 500 W: pv_ld ≥ load.

Ověření: LoadFirstDispatchTests::test_neg_sell_prep_no_fictitious_grid_import_for_load.

4.4 v35 — rampa SoC z PV B, bod T, přebytek

Tag: 2026-05-28-neg-sell-b-ramp-v35 (bod T opraven v v36 — viz níže).

Kód: _neg_sell_pv_b_charge_wh, _neg_sell_day_phases (rampa), _neg_sell_e_surplus_after_t_wh, _neg_sell_day_pv_b_usable_wh (cushion v33).

  • Zpětná projekce soc_need jen z PV B; prep soc_target[t] = soc_need[t] (ne fixních 80 %).
  • t_detach = první prep slot kde soc_need[t] ≤ soc_need[tail_start]; E_surplus_after_t od T do konce okna.
  • Prep hold: soc_prev ≥ soc_target[t]; po T: NEG_SELL_POST_DETACH_BCPV_DISCOURAGE na bc_pv.
  • solver_params.inputs: neg_sell_b_ramp_v35, t_detach_idx, e_surplus_after_t_wh, neg_sell_day_meta.

Ověření: NegSellSocPhaseTests::test_b_ramp_t_detach_and_surplus_meta, MCP solver_params.

4.5 v36 — přípravné okno neg dne

Tag: 2026-05-28-neg-prep-window-v36

Problém v35 Oprava v36
T hned na 1. sell<0 → celý den curtail A t_detachsoc_need[t] ≥ 85 % soc_max + suffix B ≥ zbytek do 100 %
Ráno 2. neg dne nabíjí místo exportu Pre-neg per den + cushion A+B; pre_neg_pv_export_slots pro každý pražský den zvlášť
Večer nevybije před zítřejším neg neg_evening_before_neg_slots — výboj večer D1

Cílová časová osa (např. 27. 5.):

0709:30  sell ≥ 0   → export FVE (pre-neg, cushion OK)
09:45+    sell < 0   → nabíjení A+B po rampě
~1113    bod T      → uvolnění / curtail A, B do domu nebo export
večer 26.5           → vybít bat před neg 27.5 (headroom)

Ověření: NegSellPrepWindowV36Tests, solver_params.inputs.pre_neg_cushion_by_day, neg_evening_before_neg_slots.

4.6 v40 — pozorované SoC pro neg-prep (Plan 5)

Tag: 2026-05-29-neg-prep-observed-soc-v40

Problém v36 Oprava v40
Cushion / večerní výboj z modelového SoC (řetězení cílů mezi dny) observed_soc_wh z telemetrie; žádné soc_est := soc_target[first_neg]
BMS výš → plán „už mám headroom“ nevidí Cushion OK pokud observed_soc ≥ soc_target[first_neg]
Večerní výboj pod exportuje Rozpočet max(0, observed reserve night_baseload_buffer)neg_evening_push_slots

Kód: _pre_neg_pv_export_bundle, _neg_evening_discharge_budget_wh, _neg_evening_before_neg_push_indices v planning_engine.py.

Ověření: ObservedSocNegPrepTests; MCP solver_params.inputs.observed_soc_wh, neg_evening_export_budget_wh, neg_evening_push_slots.


5. Specifikace rampy (v35 — reference)

5.0 Rozhodnutí produktu (home-01, 2026-05)

Téma Rozhodnutí
Rampa / T Odvozené z PV B; bez řízení fixním planner_neg_sell_prep_soc_percent v LP pro home-01.
TČ v pre-neg Zákaz plánovaného topení.
Bazén Min. 4 h filtrace/den, dynamicky navýšit; Shelly; přitop ručně / později.
Spirála Loxone; v38.
UI flex Workshop před v37 — viz § 9.1.

5.1 Kotva vzadu (tail — beze změny konceptu)

Pro každý pražský den s sell < 0:

indices = všechny sloty t kde sell[t] < 0, seřazené
last_neg = indices[-1]
tail_start = max(indices[0], last_neg - (N - 1))   # N = planner_neg_sell_full_soc_tail_slots

Pro t ≥ tail_start: cíl soc_target[t] = soc_max (případně rampa v tail mezi soc_detach a soc_max pokud N > 1).

5.2 Zpětná projekce pouze z pole B

Pro odhad nabití z B v slotu t (zjednodušený model, stejný styl jako _neg_sell_day_pv_usable_wh):

pv_surplus_b[t] = max(0, pv_b_forecast[t] - load_baseline[t] - rezerva_EV_HP)
charge_b[t] = min(pv_surplus_b[t], max_charge_power_w) × charge_efficiency × 0,25 h

Zpět od tail_start:

soc_need[last_neg] = soc_max
soc_need[t-1] = soc_need[t] - charge_b[t]     # clamp ≥ min_soc_wh

Výsledkem je soc_need[t] — požadované SoC na konci slotu t, kdyby stačilo jen B.

5.3 Bod T (t_detach) — v36

Definice (implementováno v36): první prep slot t, kde současně:

soc_need[t] ≥ max(0,85 × soc_max, 0,92 × soc_need[tail_start])
Σ charge_b[t..konec] ≥ (soc_max  soc_need[t]) × 1,05

Zrušeno (chyba v35): soc_need[t] ≤ soc_need[tail_start] — platilo vždy na začátku okna.

Interpretace:

Situace Význam
T brzy po začátku sell < 0 Dlouhé okno, B stačí → od T uvolnit A pro dům / odchylku
T těsně před tail Krátké okno → A potřebné déle, malý E_surplus_after_t
Aktuální SoC pod soc_need[t] při replanu Ještě fáze „honit rampu“ (A+B)
Rampa z aktuálního SoC nedosáhne tail ani optimisticky Slabý den — 100 % dnes nejspíš nevyjde

5.4 Plánovaný přebytek E_surplus_after_t

Pro sloty t ∈ [t_detach, last_neg]:

E_surplus_after_t = Σ_t max(0,
    pv_a_forecast[t] + pv_b_forecast[t]
    - load_baseline[t]
    - charge_to_battery_cap[t]
)

× 0,25 h (případně jen část nad tím, co jde do soc_need).

Použití:

Spotřebič Pravidlo
TČ předehřát v T Jen pokud E_surplus_after_t > práh a T je dostatečně brzy
Bazén filtrace Rozpočet hodin ≤ f(E_surplus_after_t), slunce)
Spirála (📋) Až když TČ + bazén nestačí sežrat přebytek
Export B Zbytek (zelený bonus) — lepší než -0,3 Kč/kWh, horší než vlastní spotřeba

5.5 Chování PV A po T (📋)

Ne „tvrdě urazit A v 80 %“.

Režim LP / plán
t < t_detach Plné nabíjení z A+B směrem k soc_need[t]
t ≥ t_detach Necpát A do baterie (bc_pv z A minimálně); A dostupné pro pv_ld / dům
Curtail A Měkké nebo jen při riziku zbytečného exportu A za sell < 0

Deye: reg 340 = forecast A curtail; při plném plánu bez exportu EMS 340 nemusí zapisovat (plan_skips_deye_reg340_write).

5.6 Výstupy do solver_params (📋)

Navrhované klíče v planning_run.solver_params.inputs:

Klíč Typ Popis
neg_sell_soc_ramp_wh pole soc_need[t] per slot ISO
t_detach_idx int index slotu T
e_surplus_after_t_wh float integrál přebytku
neg_sell_window_slots int délka okna
planner_build_tag string např. 2026-05-28-neg-sell-b-ramp-v35

6. Termika — TČ, TUV, spirála

6.1 Co je dnes v solveru

  • Proměnná hp[t] 0…rated_heating_power_w v bilanci load_site_expr.
  • TUV look-ahead: tuv_usage_stats, nouz pod tuv_min_temp_c, boost při poklesu pod min+5 °C.
  • Export TČ: heat_pump_enabled / heat_pump_setpoint_w v planning_interval; Modbus zápis — viz control.md (často TODO).

Není v modelu: spirála, bojler jako samostatná zátěž, teplotní stav zásobníku jako spojitá proměnná v každém slotu (jen zjednodušený tuv_pred).

6.2 Pravidla podle typu dne (📋)

Den se sell < 0

Kdy TČ / TUV
Ranní pásma před sell < 0 (pre-neg export) Netopit (kromě nouze pod tuv_min)
Uvnitř sell < 0, t < t_detach Minimum; priorita nabíjení bat
Uvnitř sell < 0, t ≥ t_detach Komfort / předehřát dle E_surplus_after_t
Večer (sprcha) Doklep na tuv_comfort_temp_c

Den bez sell < 0

  • TČ v slotech s nízkým buy a dobrým COP (poledne), ne v nejlepších exportních slotech FVE.
  • Spirála: nízká priorita — preferovat prodej FVE.

6.3 TČ vs spirála (📋)

Kritérium Preferovat TČ Preferovat spirálu
Dlouhé sell < 0, B pokryje bat Ano (COP) Ne
Krátké okno, hodně FVE „na střeše“ Částečně Ano, pokud marginal cost ≈ 0
Den bez sell < 0 Ano při dobrém COP Spíš ne

Spirála vyžaduje novou zátěž v DB + LP (flex_load_spiral[t] nebo signál Loxone).

6.4 Parametry termiky (rozhodnutí + otevřeno)

Parametr Stav Hodnota / poznámka
hp_no_run_pre_neg_export Rozhodnuto true — v pre_neg_pv_export_ts netopit (raději export FVE).
tuv_comfort_temp_c Otevřeno Např. 5052 °C — doplnit do konfigurace site.
tuv_preheat_temp_c Otevřeno Např. 5558 °C — jen v bodu T, pokud E_surplus_after_t stačí.
tuv_evening_topup_hour Rozhodnuto 19:00 Europe/Prague — večerní doklep TUV (implementace v36).
Spirála Rozhodnuto Ovládání Loxone; model v EMS až v38.

7. Bazén — filtrace a přitop (📋)

7.1 Provozní záměr (rozhodnutí home-01)

  • Filtrace ~1 kW — min. 4 h/den; více hodin, pokud E_surplus_after_t a přebytek dovolí (marginalní náklad ≈ 0).
  • Kdy: přes den ve slunečných slotech (is_daytime_pv_surplus_slot nebo obdobné); dynamicky dle cen / přebytku, ne pevné okno 0917.
  • Proč ve dni: cirkulace promíchá prohřátou hladinu.
  • Priorita: po rampě bat / od bodu T, před exportem B za sell < 0.
  • Přitop vody: mimo první verzi plánovače; začátek sezóny ručně; automatika později.
  • Exekuce: Shelly — ovládání z EMS po implementaci assetu (v37).

7.2 Napojení na E_surplus_after_t

pool_hours_max = min(
  pool_filter_hours_per_day_config,
  floor(E_surplus_after_t_wh / (1000 W × 0,25 h))
)

Rozložit do slotů s sell < 0 ∧ slunce ∧ t ≥ t_detach.

7.3 Datový model (📋)

Zatím není v db/migration. Návrh:

  • ems.asset_pool nebo rozšíření site config JSON
  • sloupce: filter_power_w, filter_hours_per_day, solar_window_start_hour, solar_window_end_hour (Prague)

7.4 LP (📋)

  • pool_filter[t] ∈ [0, filter_power_w]
  • Zapnout jen pokud: soc[t] ≥ soc_need[t], sell[t] < 0, slunce, zbývá denní rozpočet hodin
  • Penalizovat ge_pv z B při plné baterii a zapnutém bazénu

8. EV

  • Typicky odpoledne — session z telemetrie / ev_session.
  • LP: deadline constraint na target_soc k target_deadline.
  • Strategická vazba na v35: po dosažení rampy nebo v allow_charge + PV bohatých slotech — ne v ranním pre-neg exportu.
  • Konflikt s večerním exportem bat řeší stávající masky allow_discharge_export.

9. UI plánování — význam čísel

Řádek v detailu slotu (Planning.tsx):

„Škrcení A / ≈ reg 340“

Zobrazení DB / výpočet Význam
CURTAIL X W pv_a_curtailed_w Kolik W z pole A plán odebírá (nechce využít). 0 = žádné škrcení.
povoleno Y W pv_a_forecast_solver_w pv_a_curtailed_w Odhad reg 340 (max solar power) pro pole A.

Příklad: forecast A = 4654 W, curtail = 1117 W → povoleno 3537 W.

Badge sell prep / sell tail: z solver_params.masks[].neg_sell_phase (v32).

Bat. / síť / SoC: battery_setpoint_w / grid_setpoint_w / battery_soc_target_pct — po v34 u vysoké FVE grid ≈ 0, ne fiktivní import = load.

9.1 Vizualizace flexibilních zátěží — probrat před implementací (📋)

Stav: produktové rozhodnutí neníneimplementovat bazén / rozšířené TČ v UI ani v LP sinku, dokud není schválený návrh. Workshop mezi v35 a v37.

Proč: flexibilní zátěže (TČ, bazén, spirála, EV) sdílí stejnou časovou osu jako energie (T, E_surplus_after_t, fáze sell<0). Bez přehledného UI bude provoz těžko kontrolovatelný.

Návrhy k diskusi (nic z toho není závazná implementace):

Nápad Co ukázat
Pásma dne V grafu plánu: pre-neg export | sell<0 prep | od T | tail | večerní export bat.
Bod T Svislá značka + tooltip: t_detach, e_surplus_after_t_wh, odhad hodin bazénu.
Rozpočet bazénu „Dnes 2/4 h filtrace naplánováno“ + zbývající Wh přebytku.
Slot detail Kromě bat/síť/FVE: (heat_pump_setpoint_w), EV, (budoucí) bazén ON, badge flex sink.
Srovnání běhů Před/po v35: rampa SoC, méně fiktivního grid importu, curtail A.
Živě vs plán Volitelně: telemetrie TUV / Shelly pool vs plánovaný stav (až bude data).

Výstup workshopu: krátký mock / seznam widgetů v Planning.tsx + které sloupce ukládat do planning_interval / solver_params.

Otevřené otázky UI: viz docs/06-open-questions.md.


10. Priorita flexibilních spotřebičů (📋)

Při sell < 0 a plné / dostatečné baterii:

1. Bazální dům (load-first, pv_ld)
2. Nouz TUV (tuv_min)
3. EV deadline
4. TČ komfort / doklep / předehřát (dle fáze)
5. Bazén filtrace (slunce, rozpočet hodin)
6. Spirála (až bude v EMS)
7. Export pole B (zelený bonus)
8. Curtail A (poslední ventil)

11. Roadmap implementace

Pořadí Fáze Tag / doc Obsah Blokátor
1 v35 neg-sell-b-ramp-v35 Rampa soc_need z B, T, E_surplus_after_t, uvolnění A
2 UI workshop Vizualizace flex. zátěží — § 9.1; schválený návrh widgetů Před v37
3 v36 termika-v36 Blok TČ pre-neg; TUV v sell<0 po T; večerní doklep 19:00 Prague v35
4 v37 pool-v37 Bazén: Shelly, min 4 h/den, LP sink UI workshop
5 v38 spiral-v38 Spirála (Loxone) + volba TČ vs spirála v37

Každá implementační fáze: migrace (pokud DB), planning_engine.py, testy MILP, planning-changelog.md, ověření MCP na home-01.


12. Ověření v provozu

-- aktivní běh
select id, solver_params->>'planner_build_tag' as tag,
       solver_params->'inputs'->>'pre_neg_pv_export_forecast_ok' as pre_neg_ok,
       solver_params->'inputs'->>'t_detach_idx' as t_detach,
       solver_params->'inputs'->>'e_surplus_after_t_wh' as e_surplus
from ems.planning_run
where site_id = (select id from ems.site where code = 'home-01')
  and status = 'active'
order by created_at desc
limit 1;

-- sloty kolem poledne
select pi.interval_start at time zone 'Europe/Prague' as prague,
       pi.battery_soc_target_pct,
       pi.pv_a_curtailed_w,
       pi.pv_a_forecast_solver_w,
       pi.battery_setpoint_w,
       pi.grid_setpoint_w,
       pi.effective_sell_price
from ems.planning_interval pi
join ems.planning_run pr on pr.id = pi.run_id
where pr.site_id = (select id from ems.site where code = 'home-01')
  and pr.status = 'active'
  and (pi.interval_start at time zone 'Europe/Prague')::date = current_date
order by pi.interval_start;
# testy
cd backend && python3 -m pytest tests/test_planning_dispatch_milp.py -k "NegSell or PreNeg or LoadFirst" -q

13. Související soubory

Oblast Cesta
Solver backend/services/planning_engine.py_neg_sell_day_phases, _pre_neg_pv_export_*, solve_dispatch
DB parametry db/migration/V083__planner_neg_sell_phases.sql
Kontext site db/routines/R__039_fn_planning_site_context.sql
FE plán frontend/src/pages/Planning.tsxpvAAllowedW, curtail badge
Deye 340 backend/services/control/setpoints.pycompute_pv_a_reg340_max_solar_w
TUV stats ems.tuv_usage_stats, fn_update_tuv_usage_stats

14. Otevřená rozhodnutí

Živý seznam: docs/06-open-questions.md — sekce Plánování — neg sell, termika, flexibilní zátěže.

Zbývá hlavně: čas večerního doklepu TUV (~19h?), návrh UI flex zátěží (workshop před v37).