Investiční studie v2: POTENCIÁLNÍ výroba místo telemetrie (škrcení 81 %!)

Klíčová oprava (postřeh uživatele): při sell<0 lokality škrtí výrobu
(reg 340 / GEN cutoff) — telemetrie ukázala 357 kWh, predikce 1879 kWh
(96 % minut v derating). Studie nyní používají max(skutečnost, kanonický
forecast per pole) v sell<0 slotech.

Nové výsledky (horní meze): BA81 32 kWh +35/+46 Kč/den (výkon 6.25/12 kW);
KV1 25 kWh +20/+22 Kč/den (stará smlouva); HU1 fixní: 75 Kč/den bez sdílení,
149 Kč/den s EDC sdílením @1.5 Kč distribuce (sdílitelných ~49 kWh/den!);
HU1 spot: 372 Kč/den, sdílení +0. + docs/onboarding-wallbox-tc-2026-06.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-11 17:24:49 +02:00
parent d47f5f8b87
commit 53e9afb513
4 changed files with 110 additions and 4 deletions

View File

@@ -0,0 +1,35 @@
# Oživení: nový Teltonika wallbox + tepelné čerpadlo (draft 2026-06-11)
Stav: ČEKÁ NA VSTUPY od uživatele (sekce 1). Postup ověřen proti kódu
(telemetry_collector, control/outputs, views vw_asset_*_modbus_poll) a seedu
home-01 (V003). Plný checklist se vzorovými SQL: viz analýza agenta / tento dokument.
## 1. Chybějící vstupy (dodá člověk)
| Zařízení | Údaj |
|----------|------|
| Wallbox | **lokalita** (site code), IP Waveshare + unit ID, max proud (A), fáze |
| Wallbox | Modbus registry TeltoCharge: status / proud / energie (datasheet) |
| TČ | **lokalita**, model (Samsung EHS …?), IP + unit ID |
| TČ | jmenovitý výkon (W), COP@7°C, objem TUV (l), registry: stav/výkon/teploty/enable |
| Vozidlo | kapacita baterie (kWh), max AC výkon, default target SoC + deadline |
## 2. Postup (po dodání vstupů)
1. Flyway migrace `V085__seed_<site>_wallbox_tc.sql`: `site_endpoint` (modbus_tcp)
+ `asset_ev_charger` (+ `asset_vehicle`) + `asset_heat_pump` — šablony dle seedu V003.
2. Deploy (push na main) → collector čte `vw_asset_ev_charger_modbus_poll` /
`vw_asset_heat_pump_modbus_poll` → po restartu backendu začne pollovat.
3. **Pozor:** v `telemetry_collector.py` a `control/outputs.py` jsou pro
Teltoniku/Samsung části registrové mapy jako TODO — po dodání datasheetů
doplnit čtení (status/proud/energie; teploty/stav) a zápis (proud limit /
enable) přes FC dle zařízení + journal `modbus_command`.
4. Ověření: `vw_latest_ev_charger` / `vw_latest_heat_pump` (data_age < 2 min);
příjezd EV → `ev_session` + `fn_update_ev_arrival_stats`; TČ →
`tuv_delta_stats` po pár dnech (job 00:45) → solver začne TUV plánovat.
## 3. Co EMS po oživení umí hned
- EV: detekce příjezdu/odjezdu, session s deadline, plánované nabíjení v levných
slotech (v2: EV deadline constraint s placeným deficitem).
- TČ: TUV look-ahead v plánu (musí-topit okna), enable řízení exporterem.

View File

@@ -131,12 +131,34 @@ async def _load_slots(conn: asyncpg.Connection, site_id: int) -> list[Slot]:
select a.interval_start,
p.effective_buy_price_czk_kwh as buy,
p.effective_sell_price_czk_kwh as sell,
greatest(0, coalesce(a.actual_pv_production_wh,0) - coalesce(a.pv_b_production_wh,0)) as pv_a,
coalesce(a.pv_b_production_wh,0) as pv_b,
-- POTENCIÁL: při sell<0 lokalita škrtí výrobu (reg 340 / GEN cutoff),
-- telemetrie ji nevidí → použij max(skutečnost, predikce) per pole.
case when p.effective_sell_price_czk_kwh < 0
then greatest(coalesce(a.actual_pv_production_wh,0) - coalesce(a.pv_b_production_wh,0),
coalesce(fc.fc_a_wh, 0))
else greatest(0, coalesce(a.actual_pv_production_wh,0) - coalesce(a.pv_b_production_wh,0))
end as pv_a,
case when p.effective_sell_price_czk_kwh < 0
then greatest(coalesce(a.pv_b_production_wh,0), coalesce(fc.fc_b_wh, 0))
else coalesce(a.pv_b_production_wh,0)
end as pv_b,
coalesce(a.actual_load_consumption_wh,0) as load
from ems.audit_interval a
join ems.vw_site_effective_price p
on p.site_id = a.site_id and p.interval_start = a.interval_start
left join lateral (
select
sum(power_w) filter (where pa.controllable) * 0.25 as fc_a_wh,
sum(power_w) filter (where not pa.controllable) * 0.25 as fc_b_wh
from (
select distinct on (fpr.pv_array_id) fpi2.power_w, fpr.pv_array_id
from ems.forecast_pv_interval fpi2
join ems.forecast_pv_run fpr on fpr.id = fpi2.run_id
where fpi2.interval_start = a.interval_start
order by fpr.pv_array_id, fpr.created_at desc
) x
join ems.asset_pv_array pa on pa.id = x.pv_array_id and pa.site_id = a.site_id
) fc on true
where a.site_id = $1 and a.actual_load_consumption_wh is not null
order by a.interval_start
""",

View File

@@ -60,15 +60,31 @@ async def _load(conn: asyncpg.Connection, price_site: int = 3) -> list[Slot]:
else p.effective_buy_price_czk_kwh end as buy,
case when $1 = 5 then p2.effective_sell_price_czk_kwh
else p.effective_sell_price_czk_kwh end as sell,
-- POTENCIÁL: BA81 při sell<0 škrtí (81 % výroby v datech chybí)
case when p.effective_sell_price_czk_kwh < 0
then greatest(0, coalesce(a.actual_pv_production_wh,0)
- coalesce(a.actual_load_consumption_wh,0))
then greatest(0,
greatest(coalesce(a.actual_pv_production_wh,0),
coalesce(fc.fc_a_wh,0) + coalesce(fc.fc_b_wh,0))
- coalesce(a.actual_load_consumption_wh,0))
else 0 end as share_wh
from ems.audit_interval a
join ems.vw_site_effective_price p
on p.site_id = a.site_id and p.interval_start = a.interval_start
left join ems.vw_site_effective_price p2
on p2.site_id = 5 and p2.interval_start = a.interval_start
left join lateral (
select
sum(power_w) filter (where pa.controllable) * 0.25 as fc_a_wh,
sum(power_w) filter (where not pa.controllable) * 0.25 as fc_b_wh
from (
select distinct on (fpr.pv_array_id) fpi2.power_w, fpr.pv_array_id
from ems.forecast_pv_interval fpi2
join ems.forecast_pv_run fpr on fpr.id = fpi2.run_id
where fpi2.interval_start = a.interval_start
order by fpr.pv_array_id, fpr.created_at desc
) x
join ems.asset_pv_array pa on pa.id = x.pv_array_id and pa.site_id = a.site_id
) fc on true
where a.site_id = 3 and a.actual_load_consumption_wh is not null
and p2.effective_buy_price_czk_kwh is not null
order by a.interval_start

View File

@@ -0,0 +1,33 @@
# Studie navýšení baterie — perfect-hindsight nad reálnými daty (audit_interval)
# Okna 7 dní s navazujícím SoC; Δ = horní mez ročního přínosu
## BA81 (2026-04-26 … 2026-06-11; block_neg=False, pv_b_shed=True, export cap 16 kW)
current 12.5 kWh / 6.25 kW -4579 Kč / 45 dní
upgrade 32 kWh / 6.25 kW (výkon beze změny) -6144 Kč Δ +1565 Kč (+34.77 Kč/den; rok ~761512692 Kč)
upgrade 32 kWh / 12.00 kW (0.5C, cap AC stridace) -6657 Kč Δ +2078 Kč (+46.19 Kč/den; rok ~1011516858 Kč)
## KV1 (2026-04-30 … 2026-06-11; block_neg=True, pv_b_shed=False, export cap 8 kW)
current 12.5 kWh / 6.25 kW -2149 Kč / 41 dní
upgrade 25 kWh / 6.25 kW (výkon beze změny) -2952 Kč Δ +803 Kč (+19.58 Kč/den; rok ~42897148 Kč)
upgrade 25 kWh / 12.00 kW (0.5C, cap AC stridace) -3056 Kč Δ +907 Kč (+22.12 Kč/den; rok ~48448073 Kč)
Pozn.: rok = Kč/den × 365; dolní odhad ×0.6 (zima: méně PV, menší spready).
Horní mez (dokonalá předpověď) — reálný plánovač zachytí typicky 7090 %.
# HU1 BESS studie — 128 kWh / 36 kW / AC 40 kW; ceny site 3 (fixní nákup BA81)
# Období: 18 dní (BA81 audit); sdílitelný přebytek BA81 při sell<0: 882 kWh
bez EDC sdílení výnos 1351 Kč = 75.06 Kč/den (rok ~1643827396 Kč)
EDC sdílení, distribuce 2.0 Kč/kWh výnos 2251 Kč = 125.05 Kč/den (rok ~2738645643 Kč)
EDC sdílení, distribuce 1.5 Kč/kWh výnos 2689 Kč = 149.37 Kč/den (rok ~3271254520 Kč)
EDC sdílení, distribuce 1.0 Kč/kWh výnos 3130 Kč = 173.86 Kč/den (rok ~3807663460 Kč)
Pozn.: horní mez (perfect hindsight); jaro = nejsilnější sezóna pro spot spready.
# HU1 BESS studie — 128 kWh / 36 kW / AC 40 kW; ceny site 5 (SPOT nákup i prodej (site 5))
# Období: 18 dní (BA81 audit); sdílitelný přebytek BA81 při sell<0: 882 kWh
bez EDC sdílení výnos 6699 Kč = 372.17 Kč/den (rok ~81505135842 Kč)
EDC sdílení, distribuce 2.0 Kč/kWh výnos 6699 Kč = 372.17 Kč/den (rok ~81505135842 Kč)
EDC sdílení, distribuce 1.5 Kč/kWh výnos 6699 Kč = 372.17 Kč/den (rok ~81505135842 Kč)
EDC sdílení, distribuce 1.0 Kč/kWh výnos 6699 Kč = 372.17 Kč/den (rok ~81505135842 Kč)
Pozn.: horní mez (perfect hindsight); jaro = nejsilnější sezóna pro spot spready.