needed_wh i headroom z live_soc (soc_at_connect + integrál power_w), ne ze zamrzlého soc_at_connect. energy_delivered_wh se během session nikdy nezapisoval (→ needed konstantní, plánovač slepý k pokroku), counter energy_kwh (Telto reg 39) je rozbitý (17.4 kWh nabito → counter 0.18). Nový fn_ev_session_delivered_wh integruje power_w (dt cap 120 s), clamp 99 %, fallback drží staré chování bez telemetrie. Ověřeno živě: needed_wh 18750→1329, live_soc 97.9 %. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
12 KiB
EV plán zlepšení — konsolidace (2026-06-14)
Triáž z živého provozu home-01 (Tesla na TeltoCharge). Sjednocuje pět propojených pozorování do jednoho prioritizovaného plánu. Cílem je odstranit phantom nabíjení, fragmentaci a slepotu plánovače k reálnému stavu auta — bez nové „přežvykovací" vrstvy nad plánem (bolístka z v1). Veškerá logika zůstává v solveru / SQL, control vrstva je hloupý vykonavatel.
Průřezové zásady (platí pro všechny body)
- Nebudit auto. Tesla
vehicle_data(SoC/odometer/poloha) se čte jen když je auto vzhůru z vlastní vůle —get_vehicle_api_state(tesla_client.py:183, online/asleep/offline, nebudí) gatuje budicíget_charge_state(tesla_client.py:125). Výjimka bez buzení: auto, které aktivně nabíjí, je vzhůru → během session lze SoC číst bezpečně. - SQL-first. Živé SoC i „full" stav musí být v DB sloupci/tabulce; čte je
fn_ev_session_planning_json/fn_ev_session_defaults. Žádné skládání v Pythonu. - Golden gate. Cokoliv mění
planning_interval(solver, needed_wh) musí projít golden gate. Solver změny za DB flagem s default = no-op, pak kalibrace harnessem. Pozor: golden fixtures dnes EV nulují → nutná EV fixture z home-01 (chce DB). - Žádné hardcoded kódy zařízení. Vybírat podle
site_id+id. - Import/export tvrdé limity (§7/§19): control nesmí jednostranně zvednout EV
výkon nad plán (rozbilo by garanci
import ≤ max_import).
1. Živé SoC auta do plánovače — ✅ IMPLEMENTOVÁNO na dev (2026-06-14)
KOREKCE po ověření na živé DB: plánovaný coulomb z
vw_latest_ev_charger.energy_kwhNEFUNGUJE — ten counter (Telto reg 39) je rozbitý (17.4 kWh reálně nabito → counter stál na 0.18). Fix proto integrujepower_w(spolehlivý signál), ne counter. Hotovo vR__038: novýfn_ev_session_delivered_wh(time-weighted integrál power_w, dt cap 120 s) + přepočet needed_wh/headroom zlive_soc = soc_at_connect + delivered/cap(clamp 99 %), fallbackcoalesce(live, energy_delivered_wh, 0). Ověřeno živě: needed_wh 18750 → 1329 Wh, live_soc 97.9 %. Nenasazeno na prod (čeká deploy). Detail:docs/planning-changelog.md2026-06-14.
Pozorování: auto na 99 %, ale plán do rána ukazuje 4× 11 kW okna (phantom).
Příčina (ostrá, z workflow wqikxa47f — horší, než vypadala): plánovač počítá
needed_wh i headroom výhradně ze zamrzlého soc_at_connect_pct (zapsán jednou
při příjezdu) mínus energy_delivered_wh — JENŽE energy_delivered_wh se během
session NIKDY nezapíše (V006:53, NOT NULL DEFAULT 0; fn_ev_session_transition jen
otevře/zavře, telemetry_collector píše kumulativní energii jen do
telemetry_ev_charger.energy_kwh, ne do session). Takže delivered je trvale 0 →
needed_wh = (target − soc_at_connect)/100 × cap je KONSTANTA po celou session,
neklesá jak auto nabíjí. Plánovač je k pokroku nabíjení úplně slepý → rolling
replan každých 15 min znovu emituje plný deficit (4× 11 kW okno). (R__038:37–61.)
Klíč: živý progres už v DB MÁME — telemetry_ev_charger.energy_kwh (Teltonika
reg 39, kumulativní kWh per session, reset na novou session, poll 60s), vystavený přes
vw_latest_ev_charger.energy_kwh. Hardwarově měřený, funguje pro všechna auta
(i Zoe), bez Tesla API, bez buzení.
Fix #1 (primární — čistě SQL, žádná nová vrstva/tabulka/job, žádné buzení):
v fn_ev_session_planning_json (R__038) nahradit zamrzlý coalesce(es.energy_delivered_wh, 0)
živým coalesce((select energy_kwh from vw_latest_ev_charger … otevřená session) × 1000, es.energy_delivered_wh, 0). Odvodit live_soc = soc_at_connect + delivered/(cap_wh)×100,
clamp 99 % (taper ignorujeme). needed_wh = greatest(0, (target − live_soc)/100 × cap_wh)
a headroom = greatest(0, (99 − live_soc)/100 × cap_wh) z živého SoC. → needed i headroom
klesají s nabíjením a kolabují na 0 při plném autě → phantom okna zmizí.
- Fallback
coalesce(vw, es.energy_delivered_wh, 0)drží staré golden fixtures beze změny (bez WB telemetrie = delivered 0 = soc_at_connect = dnešní chování) → golden gate zůstane zelená by-construction. Tj. #1 lze nakódovat a lokálně dokázat ne-regresi bez živé DB; DB chce jen živé ověření na home-01.
Komplement — ne-Tesla (Zoe): dnes se session při soc_at_connect_pct IS NULL
úplně vyřadí z LP (R__038:29). Změkčit: startovní SoC z kaskády (ruční UI patch
přes R__015 → zděděný soc_at_disconnect minulé session → konzervativní default ~20 %),
pak coulomb delta dá použitelné absolutní SoC. (samostatný krok po ověření #1.)
Odloženo (Fix #3, opt-in): mid-session Tesla refresh živého SoC — JEN když coulomb
counter nestačí (auto nabito mimo WB / chybí WB telemetrie). Budí auto (vampire drain,
proti dnešní zásadě) → nedělat, dokud se coulomb fix neukáže jako nedostatečný. Wallbox
charging_state „full" je univerzální brzda zdarma navrch (auto přestalo brát → needed
spadne i kdyby coulomb plaval).
Soubory: R__038_fn_ev_session_planning_json.sql (jádro), R__015 (Zoe patch),
docs/04-modules/ev-charging.md, docs/planning-changelog.md. Golden: ANO (mění
needed_wh) — fallback drží fixtures bez EV telemetrie identické; fixtures s nenulovou WB
telemetrií se přegenerují (phantom byl bug, nová čísla správná). Roll-forward deficitu
vyjde emergentně: nenabito dnes → SoC nízký → další deadline dožene.
Ověřit na živé DB (chce IP): že vw_latest_ev_charger.energy_kwh sedí na AKTUÁLNÍ
session (counter per connector — spolehlivě resetuje na session?); reálná AC→DC účinnost
(~8–12 % ztrát → live SoC mírně optimistické, žádoucí směr — méně phantom); porovnat
odvozené live_soc vs 99 % na displeji auta a že needed_wh/headroom spadnou na ~0.
2. Předehřev / 0 A logika — PRIORITA Č. 2 (control, bez golden)
Princip: wallbox neumí oddělit „proud na topení" od „proudu na nabití".
- SoC ≥ target → NEřezat na 0 A. Pusť proud → Tesla se předehřeje z WB (levná síť/baterka) místo z vlastní (vožené, drahé) baterie; protože je na targetu, baterku stejně nenabije → nulové riziko. Hlavní zimní případ.
- SoC < target → řídí plán (nabít v levných, 0 A v drahých). Konflikt předehřevu v drahém slotu je vzácný (auto obvykle dosáhne targetu přes noc) → nepřekomplikovávat.
- Operačně: odpojení → jednorázové 0 A (auto pryč, failsafe je jedno, žádné periodické psaní); připojení → notifikace → plán + amps; po dobu připojení → re-assert amps každý tick (Fáze-0 oprava proti driftu WB watchdogu na failsafe).
- Fáze z DB (
asset_ev_charger.phases), ne hardcoded 3/1 (setpoints.py:185-186).
Závisí na #1 (potřebuje znát SoC ≥ target). Soubory: setpoints.py,
outputs.py, docs/04-modules/ev-charging.md. Golden: NE (jen překlad
watt→amp při zápisu; control nečte/nepíše planning_interval).
3. Anti-fragmentace + plný výkon v solveru — PRIORITA Č. 3 (za flagem)
Pozorování: nabíjení rozsekané přes 21:15 / 1:30 / 1:45 / 5:30 / 6:00, navíc dílčí 1,3–1,4 kW. „Z baterky je solveru jedno kdy" (uživatel) = indiference → náhodný scatter.
Příčina: EV je v solveru spojitá energie po slotech bez jakéhokoliv časového
členu — žádná start/stop ani commitment penalta (tu má jen baterie). Pro LP je
souvislý i roztříštěný profil ekonomicky identický (solver_v2.py:292-337).
Dílčí výkon = marginální slot dolitý na zbytek (spojitá proměnná, :162-175).
Fix (jeden člen v objektivu, žádná nová vrstva):
- Block-start penalta:
ev_start[t] ≥ on[t] − on[t-1], objektiv+ Σ ev_start × planner_ev_start_penalty_czk. Min počet startů = jedna várka. Protože scatter z baterie je čistá remíza, malá penalta slepí blok zadarmo a nikdy nepřebije reálný cenový spread (auto-splnění obavy „ať to nehrne přes extrémní cenu"). DB param naasset_ev_charger, default 0 = no-op. min_power_w→ třífázový floor (6 A × 3 × 230 ≈ 4140 W) místo jednofázových 1380 → zruší sub-6 A drobky i tiché shození pod minimem (outputs.py:49je správně, problém je nefyzikální setpoint z plánu).
Soubory: solver_v2.py, db/migration/V1xx__asset_ev_charger_ev_start_penalty.sql,
R__039, db_io.py, golden fixtures, docs/04-modules/planning.md,
docs/planning-changelog.md. Golden: ANO (za flagem default 0 → no-op).
Odloženo zvlášť: explicitní round-trip cena EV-z-baterie v LP (citlivé na
arbitráž §8; na scatter nemá vliv).
4. Trip/usage forecast — aktivace (PRIORITA Č. 4, většinou jen config)
Stav: postaveno (V089 + R__096), chytřejší než původní nápad:
ev_vehicle_obs(Tesla obs při příjezdu/odjezdu),ev_trip(km z odometru, kWh z ΔSoC,charged_awayvyloučí nabíjení cestou),ev_usage_stats(týdenní DOW rytmus), job 00:50 (lifespan.py:276 fn_update_ev_usage_stats).fn_ev_required_soc= P80 spotřeby toho DOW + 10 p.b., clamp[min_target_soc_pct, 100];fn_ev_next_departure= typický odjezd.- Model je DOW-based, ne GPS-route — GPS okruhy zatím nedělá (refinement, nízká priorita; DOW na dojíždění většinou stačí).
Co dotáhnout:
- Ověřit objem dat (≥4 vzorky/DOW; telemetrie od ~3/2026 → po 3 měsících by mělo stačit) — chce živou DB.
- Zvážit zapnutí
asset_vehicle.target_soc_forecast_enabled(default false = sbírá, ale session jede na defaultech).
Golden: NE (jen nastaví target/deadline session). Soubory: žádné nové, jen verifikace + flag.
5. Geofence arrival trigger — PRIORITA Č. 5 (schváleno uživatelem)
Motivace: dnes je celý arrival/trip ukotvený na píchnutí do wallboxu.
Když uživatel nepíchne (zaparkuje doma bez nabíjení), wallbox nevidí nic → žádný
trip, žádná obs. Presence cesta (V095) přitom „je doma" detekuje přes GPS
geofence i bez píchnutí (telemetry_collector.py:840-849 at_home).
Fix: přechod at_home false→true (auto vzhůru, nepíchnuté) brát jako
arrival home event:
- zapsat obs pro trip-building (i bez píchnutí), s
triggerrozlišujícím zdroj (wallbox vs geofence); - umožnit proaktivní notifikaci (bod #6) i bez píchnutí.
Caveaty: oportunistické (jen když je auto vzhůru → ne instantní); debounce
(2–3 vzorky); dedup s wallbox arrival (když píchneš, wallbox je autoritativní,
geofence se nepočítá dvakrát); trip se páruje s nejbližším relevantním
odjezd-eventem. Soubory: telemetry_collector.py (presence cesta),
R__096 (fn_ev_build_trips přijme geofence arrivals), případně nový trigger
enum ve V089 schématu (nová migrace). Golden: NE (jen sběr dat/notifikace).
6. Proaktivní notifikace „doma + nenabito + levné" — PRIORITA Č. 6
Datový základ existuje (ev_presence_obs: at_home, charging_state, SoC,
vše bez buzení). Logika: at_home=true ∧ nepíchnuto (charging_state disconnected)
∧ SoC < target ∧ (přebytek PV NEBO záporná/levná cena) → Discord nudge „píchni ho,
je levno". Oportunistické (čeká, až je auto vzhůru). Napojí se na #5 (geofence
arrival) a stávající ev_notify / discord_bot. Golden: NE.
Pořadí nasazení
- #1 živé SoC — odstraní phantom okna a plýtvání; enabler pro #2. (golden)
- #2 předehřev/0 A — control, hned po #1, bez golden.
- #3 anti-fragmentace — za flagem default 0, kalibrace harnessem + EV fixture.
- #5 geofence arrival + #6 notifikace — sběr/notifikace, samostatné PR.
- #4 forecast aktivace — až je dat dost (verifikace na DB).
Blokery: #1, #3, #4 chtějí živou DB (EV fixture, objem dat, ověření) —
potřebuju IP serveru (frank se neresolvuje). Lokálně umím dokázat jen
ne-regresi (golden default off) + unit testy.
Rozhodnutí (z rozhovoru 2026-06-14)
- 3 fáze (ne 1f surplus — pokryje velká baterka).
- Anti-fragmentace = malá ekonomická penalta v solveru, ne tvrdá priorita ani nová vrstva; control zůstává hloupý (žádný max-amps override — rozbil by §7).
- Geofence arrival ANO (robustnost bez píchnutí).
- DOW forecast stačí; GPS-route clustering odloženo.