EV arrival hold: po detekci píchnutí okamžitě 0 A — nabíjení spouští až plán
All checks were successful
CI and deploy / migration-check (push) Successful in 28s
CI and deploy / deploy (push) Successful in 1m15s

TeltoCharge po připojení kabelu sám rozjede nabíjení svým defaultem; EMS ho
dosud dohnal až exportem setpointů (do 15 min). _on_ev_arrival nyní před
replanem zapíše přes journal telto_amps_to_use=0 (write_ev_arrival_hold),
replan+export vzápětí nastaví plánované ampéry. Watchdog (300 s → failsafe
8 A) zachován — výpadek EMS auto nenechá na 0 A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-12 19:52:10 +02:00
parent 80623573ea
commit 5530253662
2 changed files with 61 additions and 0 deletions

View File

@@ -128,6 +128,56 @@ async def write_ev_setpoints(
return f"OK EV: {written}/{len(rows)} charger(s) written"
async def write_ev_arrival_hold(
site_id: int, charger_code: str, db: asyncpg.Connection
) -> bool:
"""Okamžitě po DETEKCI příjezdu zapsat 0 A na daný wallbox (přes journal).
TeltoCharge po připojení kabelu sám rozjede nabíjení svým defaultem —
nabíjet smí až PLÁN (replan + export běží hned poté v _on_ev_arrival,
takže držení trvá sekundy až ~1 min). Watchdog registry se zapíší spolu
s 0 A (drop-unchanged je po prvním verified stejně přeskočí).
"""
from services.control.modbus_journal import (
create_modbus_commands,
execute_modbus_commands,
)
row = await db.fetchrow(
"""
SELECT ec.id AS asset_id, ec.code, se.host, se.port, se.unit_id
FROM ems.asset_ev_charger ec
JOIN ems.site_endpoint se ON se.id = ec.endpoint_id
WHERE ec.site_id = $1
AND ec.code = $2
AND ec.schedulable = true
AND se.enabled = true
AND se.endpoint_type = 'modbus_tcp'
""",
site_id,
charger_code,
)
if row is None:
return False
cmd_ids = await create_modbus_commands(
site_id,
None,
"ev_charger",
int(row["asset_id"]),
str(row["code"]),
str(row["host"]),
int(row["port"] or 502),
int(row["unit_id"] if row["unit_id"] is not None else 1),
_telto_setpoint_registers(0),
db,
)
ok = await execute_modbus_commands(cmd_ids, db)
logger.info(
"EV arrival hold [%s]: 0 A %s", charger_code, "written" if ok else "FAILED"
)
return bool(ok)
async def write_heat_pump_setpoint(
site_id: int, setpoints: ControlSetpoints, db: asyncpg.Connection
) -> str:

View File

@@ -87,6 +87,17 @@ async def _on_ev_arrival(site_id: int, charger_code: str) -> None:
from services.planning_engine import run_rolling_replan
async with _BG_POOL.acquire() as conn:
# Wallbox po píchnutí sám rozjíždí nabíjení svým defaultem — držet
# 0 A, dokud nerozhodne plán (export běží hned po replanu níže).
try:
from services.control.outputs import write_ev_arrival_hold
await write_ev_arrival_hold(site_id, charger_code, conn)
except Exception:
logger.exception(
"EV arrival hold failed (site=%s, %s) — WB pojede defaultem do exportu",
site_id, charger_code,
)
# Tesla: skutečné SoC do session PŘED replanem (selhání nesmí blokovat plán)
try:
await _patch_session_from_tesla(site_id, charger_code, conn)