Slabý server: dict (tabulka, asset_id) → (signature, last_stored_at);
_idle_skip ukládá vždy při změně signature, aktivitě, po startu procesu
a heartbeat po > 840 s (každý 15min bucket má ≥ 1 řádek).
- telemetry_ev_charger: aktivní = status != 'available' nebo power > 50 W;
signature (status, výkon na 100 W)
- telemetry_pool_pump: aktivní = is_on nebo power > 5 W (ON řádky 1/min
kvůli on_minutes); signature (is_on, výkon na 10 W)
- telemetry_loxone_sensor: jen změna hodnoty ≥ 0.1 / heartbeat
- telemetry_heat_pump: aktivní = mode != 'off' nebo defrost; signature
(mode, teploty na 0.2 °C)
- telemetry_inverter: beze změny — NIKDY se nepřeskakuje (audit Wh split,
baseline, SoC plánovače)
Detekce příjezdu/odjezdu EV: previous_status přesunut z posledního řádku DB
do in-memory _EV_LAST_STATUS (po startu seed z vw_latest_ev_charger —
přechod během výpadku se pozná, prázdná DB nevystřelí falešný příjezd);
fn_ev_session_transition se volá jen při změně statusu.
PoolCard: staleness práh 5 → 16 min (> heartbeat 840 s).
Docs: telemetry.md sekce „Idle-skip zápisů" (pravidla pro nové čtecí dotazy:
sumy/gapfill, ne avg přes řádky), planning-changelog (TUV °C/min).
Testy: tests/test_telemetry_idle_skip.py — _idle_skip jednotkově + EV
arrival/departure přežije skip i restart procesu (303 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- fn_fill_audit_interval: EV a TČ agregace sum(power_w)/15 místo avg přes
přítomné řádky — avg by při řídké telemetrii nadhodnotil aktivitu části
slotu; chybějící minuta = 0 W (idle). TČ drží NULL bez power_w (MIM-B19N).
- fn_update_tuv_usage_stats: delta TUV normalizovaná na °C/min délkou mezery
mezi řádky (gap_min), mezery > 30 min vyloučeny; pro hustá 1min data
numericky identické s původním LAG.
- vw_pool_pump_day_energy: komentář — on_minutes drží invariant „zapnuté
čerpadlo se ukládá každou minutu".
Pro hustá 1min data beze změny výsledků; připravuje idle-skip zápisů
v telemetry_collector (navazující commit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nahrazuje placeholder IP 192.168.1.101/.102 ze seedu; sdílená sběrnice
s plánovaným Chint elektroměrem (unit 3). Teltonika strana čeká na povolení
Modbusu v aplikaci.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Skutečná příčina 600k buffers: CTE factor/factor_raw (single-ref) PG inlinuje
do projekce with_factor → fn_pv_forecast_correction_factor (48 ms / 1.9k
buffers) se vyhodnocovala ~300× per výstupní slot. Plan cache s tím neměl nic
společného (dřívější count(*) měření projekci zahodilo, proto vycházelo
0.42 s). Faktor se teď počítá jednou do v_rolling_factor a vkládá jako
literál (13. argument format).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
set plan_cache_mode=force_custom_plan na SQL funkci v PG 18 nezabral (stále
generický plán, 4.5-9 s / 600k buffers). plpgsql EXECUTE s literály = vždy
čerstvý plán: tělo s konstantami změřeno 0.42 s / 34k buffers. Signatura,
chování i výstup beze změny.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1) full_status._iso_utc dostával z JSONB bundle stringy → AttributeError → 500
celého /status/full; nyní parsuje přes _parse_ts.
2) /plan/compare: NameError — 'comparison = _bundle_from_current(compare_raw)'
se nikdy nesestavilo (smazaný řádek), endpoint vždy 500.
3) fn_forecast_pv_slots_range_canonical_ab: PG 18 cachuje plány SQL funkcí →
generický plán 4.5 s / 607k buffers; set plan_cache_mode=force_custom_plan
→ 0.4 s / 34k (změřeno explain analyze na živé DB). Táhne /plan/current,
/plan/compare i rolling plánovač.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Větev 'slabý úsvit (forecast<1500 W) → reg 340 neposílat' zastínila větev
'buy<0 a sell<0 + pole B → pv_a_allowed=0' — při hluboce záporných cenách
za úsvitu by se pole A nezavřelo. Prohozeno pořadí; opravuje pre-existing
fail test_neg_buy_and_sell_with_pv_b_forces_pv_a_off (293 passed, 4 xfail).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Endpoint GET /sites/{id}/forecast/pv-slots-corrected (dashboard) volal těžkou
fn_pv_forecast_delta_profile inline (~44 s/site na prod) — uživatel: 45 s
response, telemetry endpoint vyhladověl. Kanonická plánovací řada (R__088) už
cache používala, R__079 ne. Cache je pro všechny 4 lokality naplněná, čtenář
po HOTFIX 2/2 nikdy nepočítá.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_apply_export_plan_guard při export_mode=NONE (plán nabíjí baterii, neexportuje)
vynucoval _passive_no_export_guard s export_ban=True + deye_gen_cutoff_enabled=True
bez ohledu na cenu -> reg 178 bity 0-1=3 (MI cutoff) + reg 145=0 a mikroinvertory
(pole B) fyzicky stály i při sell +1.36 Kč (BA81 dnes: gen port ~0 W od 12:16Z,
SoC 64 %, stringy 4.2 kW do baterie). Tvrdý ban nově JEN při záporné vykupní;
při kladné guard dál drží PASSIVE/143=0/baterie nevybíjí do sítě, ale MI jedou
(absorbce do baterie, přetok se prodá). Plánový z_gen_cutoff se respektuje.
Pre-existing fail test_neg_buy_and_sell_with_pv_b_forces_pv_a_off padá i na main
(pv_a_allowed_w None != 0) — nesouvisí, řešit zvlášť.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Od uživatele po 42883 incidentu (R__018 comment na staré signatuře shodil
2 deploye): odkaz přes signaturu se rozbije při každé změně parametrů.
R__018 převeden na bez-parametrovou formu, pravidlo v CLAUDE.md Konvencích.
Zbylých 51 parametrizovaných comment on / 6 dropů v repu funguje (míří na
aktuální signatury) — normalizovat při dotyku.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Throttle commit změnil signaturu fn_refresh_site_pv_delta_profile_cache na
(int, boolean default false), ale comment on function dál mířil na (int) →
repeatable migrace selhala (function does not exist), oba deploye (7da7205
i 18bf93a) spadly — na produkci NENÍ nic z delta hotfixů. PATCH kalibrace
nově volá refresh s p_force=true (throttle nesmí zadržet přepočet po změně
parametrů).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Postřeh uživatele odhalil druhou půlku problému: _cached getter měl
max_age 30 min s INLINE fallbackem na plný 44s přepočet — dosud to maskoval
15min refresh; po throttlu refresh jednou za 6 h by KAŽDÉ čtení po
vystárnutí cache (plan/current, canonical sloty plánovače) spouštělo 44 s.
Čtenář teď vrací cache bez ohledu na stáří; počítá výhradně refresh
(throttle 6 h + denní catch-up). Inline jen first-run/analytická okna.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- list poll 5→10 min; vehicle_data JEN při přechodu asleep→online nebo
max 1×/15 min (data /usr/bin/zsh.002/req); wake nikdy (gate na online)
- otevřená ev_session = auto u wallboxu → ŽÁDNÉ API cally (při AC nabíjení
auto nespí — bez gatu by data tekla celou noc nabíjení)
Odhad: </měsíc (online okna mimo wallbox jsou krátká).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
všechny deploye od ~13:40 (R__082 OTE fix, V094, bot fallback ve frontě).
V093 nebyla aplikována (transakční rollback) — úprava failed migrace je
legitimní (immutability platí pro APLIKOVANÉ); na serveru nutný jednorázový
'flyway repair' (smaže failed záznam z historie).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Všechny site webhook URL jsou NULL a env DISCORD_WEBHOOK_URL nebyl nastaven
→ send_discord vždy skončil na 'not configured, skipping' (uživatel: 'nikdy
mi nic nepřišlo'). Fix: fallback přes bota (REST POST do DISCORD_EV_CHANNEL_ID
— token už v .env, žádný webhook netřeba); webhook má přednost, kdyby ho
uživatel později nastavil per site. Bot navíc při startu pošle '✅ online'
(viditelný důkaz, 1× per deploy).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
OTE import dnes spadl (InvalidParameterValueError: unrecognized format()
type specifier '.') — denní cenové signály poprvé trefily větev s %.3f/%.2f;
PG format() zná jen %s/%I/%L. Náhrada %s + round(x, N) ve všech 7 výskytech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
400 invalid_grant = spálený token rotací NEBO ~10min výpadek po revokaci
souhlasu (Tesla) — místo tracebacku log s návodem a return None (EMS jede
dál na defaultech). deploy/tesla/reauth.sh: authorize URL → výměna → DB →
ověření v jednom kroku (žádná příležitost pro rotační past).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V dev portálu je registrováno https://ems.vojacek.eu/t-auth — docs i setup
skript diktovaly /tesla/callback (mismatch = invalid_auth_code / chybné
návody). Caddy blok nově servíruje callback stránku na /t-auth (rewrite).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Postřeh uživatele — pásma nízkého výkupu před zápornými okny: mechanismus
měkkého cíle to už řeší (opp 1 Kč/kWh > sell 0.3 → auto vyhraje), test
to dokládá: plná domácí baterka + 9 kW PV → ~17 kWh do auta, minimální export.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uživatel: 'potřebuju do X % (tvrdý), ale klidně dobij na 100 % když je to
skoro zadarmo; při záporných cenách radši do auta než nechat na střeše'.
- V094 asset_vehicle.opportunistic_value_czk_kwh (default 1.0; = hodnota
ušetřeného BUDOUCÍHO nabíjení — auto neumí zpět, žádný noční prodej)
- R__039 ev_sessions: + headroom_wh ((100−target) % kapacity) + opp value;
session se nenuluje po dosažení targetu, dokud má headroom
- solver_v2: dekompozice Σ(EV) == needed − unmet + opp, opp ∈ [0, headroom],
odměna opp×value; zároveň FIX latentního bugu — při buy<0 chyběl strop
celkové energie do auta (model mohl pumpovat bez limitu)
- 3 testy (neg ceny sají nad target po strop; běžné ceny ne; cap při opp=0);
eval fixtures beze změny (sessions null)
Víkend (pátek nízký tvrdý cíl + víkendová negativa → samo doplní do 100 %)
vyplývá z mechanismu, žádná speciální logika.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Model Y 2025 Standard RWD s LFP: menší pack, ale pravidelné 100 % je žádoucí
(balancování). Kapacita vstupuje do energy_needed a EV usage statistik.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- R__097: vw_latest_pool_pump + vw_pool_pump_day_energy (denní kWh z delty
čítače, minuty běhu) + ems_anon granty
- PoolCard na Dashboardu: stav/W/dnešní kWh+hodiny/7denní mini sloupce
- _notify_ev_arrival_plan: po příjezdu EV Discord souhrn (SoC auta → cíl,
deadline, nabíjecí okna shlukovaná ze slotů aktivního plánu, ø cena)
- docs/discord-ev-interaction.md: fáze B (bot s tlačítky přes gateway —
žádný veřejný endpoint; čeká na DISCORD_BOT_TOKEN od uživatele)
- docs: pool-shelly + ev-charging aktualizovány (pravidlo docs 1:1)
První commit na dev větvi (nová kadence: deploy až s milníkovým merge).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tři rychlé pushe dnes = 3 deploye ve frontě a vynechané rolling ticky.
Workflow: dev přidán do validačních větví (deploy zůstává jen main).
Pravidlo + deploy okno v CLAUDE.md Konvencích.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
KV1 pozorování uživatele: ráno baterie na 11 % (min 10), prodává se do sítě
— nenadálý odběr/mrak by se kupoval za fixních 6.35. v1 mělo denní rampu
(safety_soc_target_wh z R__063: reserve 30 % ráno → reserve+noc večer,
6-19 h, flag planner_daytime_charge_target_enabled) — v2 ji ignoroval.
Mechanismus (vzor nočního polštáře): deficit pod rampou platí za KAŽDÝ slot
nájem buy×faktor (V091 asset_battery.planner_safety_soc_risk_factor,
default 0.05; 0=vypnuto) → ráno se nejdřív doplní rezerva (4 h deficitu
1 kWh při buy 6.35 ≈ 5.1 Kč > sell ~2.5), extrémní sell špička smí deficit
racionálně podstoupit. R__039 + db_io + 2 testy (KV1 scénář, spike).
Eval fixtures beze změny (sloupec v context_json fixtures není → 0);
živá produkce dostane faktor přes fn_planning_site_context.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v1 to řešil rampou (plný výkon než se řeže pole A — zelený bonus B, riziko
večerního mraku). v2 byl k načasování v okně sell<0 indiferentní (PV zdarma
kdykoliv) a směl nabíjení odložit — odklad ale spoléhá na predikci.
Mechanismus: malá prémie za držení energie dřív (objective −= soc[t] ×
frontload v neg slotech). Rozbíjí indiferenci směrem k front-loadu, nikdy
nepřebije skutečné ceny. Velikost z DB: asset_battery.
planner_pv_risk_frontload_czk_kwh (V090, default 0.01; 0 = vypnuto),
přes fn_planning_site_context (R__039). Test: 4 sloty plným tempem od startu.
Eval fixtures beze změny (sloupec v nich není → 0).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Postřeh uživatele: v1 držel přes noc rezervu nad min_soc (chyba predikce
noční spotřeby = neplánovaný drahý nákup); v2 slot fieldy night_baseload_*
ignoroval a směl plánovat vybití až na min_soc.
Mechanismus ve filozofii v2 (riziko jako cena, ne okno/penalta):
soft floor soc[t] >= min_soc + night_baseload_buffer_wh[t] (z DB
planner_night_baseload_buffer_percent, počítá R__063, klesá k 0 do rána);
porušení placené buy cenou slotu → extrémní sell špička smí polštář
racionálně prodat, běžná noc ne (buy > sell).
Eval na fixtures: v2 stále lepší na všech (+221.9 Kč vs v1; −10 Kč proti
stavu bez polštáře = cena robustnosti). BONUS: těsnější LP zrychlil extrémní
fixtures z 10 s timeoutu na 0.3–2.6 s. +3 testy (drží/spike prodá/feasible).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pravidlo 15: měřená řízená zátěž nesmí špinit bazální křivku — dosud se
odečítalo jen EV a TČ. Ruční chod čerpadla (vysávání…) i plánovaná filtrace
se nyní přiřazují zařízení, ne bazálu.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- services/tesla_client.py: access token s cache + ROTACE refresh tokenu do
ems.tesla_token (env jen seed — Tesla refresh token je jednorázový),
vehicles → vehicle_data?endpoints=charge_state, 408 (spící auto) = tiché
přeskočení, výběr vozidla dle VIN / jediného na účtu (VIN se auto-naučí)
- hook _patch_session_from_tesla v _on_ev_arrival: PŘED replanem doplní
soc_at_connect_pct (+ target z charge_limit_soc) do otevřené session přes
fn_ev_session_apply_patch (rozšířena o soc_at_connect_pct) — energii si
odvodí fn_planning_site_context (SQL-first); selhání neblokuje replan
- V086: asset_vehicle.vin, api_type='tesla' pro tesla-my (Model Y, home-01),
singleton ems.tesla_token; R__095: fn_tesla_token_get/upsert,
fn_tesla_arrival_context, fn_vehicle_set_vin
- config: TESLA_CLIENT_ID/SECRET/REFRESH_TOKEN (prázdné = vypnuto)
- testy parserů; plná sada beze změny
Aktivace: env do /opt/ems-deploy/.env + recreate backendu (docs/tesla-fleet-api.md §Stav).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Run 368/369: flyway validate 9,5 min nedostal spojení k db (EOF) — server
zadušený běžícím stackem + buildem. Stop backend/frontend/postgrest hned po
compose config; db zůstává pro flyway. Workflow self-install už funguje
(ověřeno v logu 369: nový skript se použil).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Doména slouží jen jako veřejná vizitka pro Tesla: Caddy blok vystavuje POUZE
.well-known public key a statickou OAuth callback stránku (zobrazí ?code=,
nic neodesílá), vše ostatní 404 — EMS zůstává na VPN. Certifikát Let's Encrypt
řeší hostovský Caddy automaticky.
deploy/tesla/setup_tesla_domain.sh (spustit NA SERVERU): EC keypair prime256v1
(privátní do /opt/ems-deploy/secrets 0600), public do .well-known, callback,
vypíše Caddy blok. docs/tesla-fleet-api.md: developer portál, partner
registrace, OAuth flow, plán EMS integrace (tesla_client + hook v _on_ev_arrival).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>