Presence watcher: cost-aware pacing (Fleet API je placené)
All checks were successful
CI and deploy / migration-check (push) Successful in 48s
CI and deploy / deploy (push) Has been skipped

- 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>
This commit is contained in:
Dusan Vojacek
2026-06-12 14:19:41 +02:00
parent 2122fa2035
commit e490e8cd26

View File

@@ -499,12 +499,19 @@ async def poll_loxone_sensors(site_id: int, db: asyncpg.Connection) -> None:
)
#: presence poll pacing (sekundy) a geofence poloměr (m)
EV_PRESENCE_POLL_S = 300
#: presence poll pacing (sekundy) a geofence poloměr (m).
#: Fleet API je placené (vehicle_data $0.002/req, wake $0.02 — wake NIKDY):
#: list 10 min; vehicle_data jen při přechodu asleep→online a pak max 1×/15 min;
#: při otevřené ev_session se nepolluje vůbec (auto je u wallboxu = doma,
#: a při AC nabíjení nespí — bez gatu by data cally tekly celou noc).
EV_PRESENCE_POLL_S = 600
EV_PRESENCE_DATA_MIN_S = 900
EV_PRESENCE_HOME_RADIUS_M = 150
#: anti-spam "píchni auto": min. rozestup notifikací per vozidlo (s)
EV_PLUG_NUDGE_COOLDOWN_S = 2 * 3600
_EV_PRESENCE_LAST_POLL: dict[int, float] = {}
_EV_PRESENCE_LAST_DATA: dict[int, float] = {}
_EV_PRESENCE_LAST_STATE: dict[int, str] = {}
_EV_PLUG_NUDGE_LAST: dict[int, float] = {}
@@ -542,6 +549,19 @@ async def poll_tesla_presence(site_id: int, db: asyncpg.Connection) -> None:
if veh is None or veh["latitude"] is None:
return
# auto na wallboxu (otevřená session) = doma; žádné API cally (šetří $)
plugged = await db.fetchval(
"""
select exists(
select 1 from ems.ev_session es
where es.vehicle_id = $1 and es.session_end is null
)
""",
int(veh["id"]),
)
if plugged:
return
from services.tesla_client import get_charge_state, get_vehicle_api_state, haversine_m
try:
@@ -552,11 +572,20 @@ async def poll_tesla_presence(site_id: int, db: asyncpg.Connection) -> None:
if api_state is None:
return
prev_state = _EV_PRESENCE_LAST_STATE.get(int(veh["id"]))
_EV_PRESENCE_LAST_STATE[int(veh["id"])] = api_state
woke_up = api_state == "online" and prev_state != "online"
data_due = (
loop_now - _EV_PRESENCE_LAST_DATA.get(int(veh["id"]), 0.0)
>= EV_PRESENCE_DATA_MIN_S
)
at_home = None
distance_m = None
charging_state = None
shift_state = None
if api_state == "online":
if api_state == "online" and (woke_up or data_due):
_EV_PRESENCE_LAST_DATA[int(veh["id"])] = loop_now
try:
st = await get_charge_state(db, veh["vin"])
except Exception as e: