Tesla presence watcher: geofence, ev_presence_obs, 'píchni auto' pobídka
- V095 ems.ev_presence_obs (state/at_home/distance/charging/shift per ~5 min) - tesla_client: get_vehicle_api_state (jen /vehicles — nebudí), haversine_m - collector poll_tesla_presence: online → poloha → geofence 150 m vs GPS site; přechod pryč→doma + Disconnected → Discord pobídka s aktuálním přebytkem (cooldown 2 h); vše logováno pro budoucí dostupnostní statistiku - 6 testů (haversine, přechody); docs: zákopy reauth procesu (6 bodů) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -166,3 +166,33 @@ async def get_charge_state(
|
||||
return None
|
||||
r.raise_for_status()
|
||||
return parse_charge_state(r.json())
|
||||
|
||||
|
||||
def haversine_m(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||
"""Vzdálenost dvou GPS bodů v metrech (čisté, testovatelné)."""
|
||||
import math
|
||||
|
||||
r = 6_371_000.0
|
||||
p1, p2 = math.radians(lat1), math.radians(lat2)
|
||||
dp = math.radians(lat2 - lat1)
|
||||
dl = math.radians(lon2 - lon1)
|
||||
a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
|
||||
return 2 * r * math.asin(math.sqrt(a))
|
||||
|
||||
|
||||
async def get_vehicle_api_state(db: asyncpg.Connection, vin: str | None) -> str | None:
|
||||
"""Jen state z /vehicles (online/asleep/offline) — NIKDY nebudí auto."""
|
||||
token = await _get_access_token(db)
|
||||
if token is None:
|
||||
return None
|
||||
async with httpx.AsyncClient(
|
||||
timeout=HTTP_TIMEOUT_S, headers={"Authorization": f"Bearer {token}"}
|
||||
) as client:
|
||||
r = await client.get(f"{API_BASE}/api/1/vehicles")
|
||||
r.raise_for_status()
|
||||
vehicles = r.json().get("response") or []
|
||||
if vin:
|
||||
v = next((x for x in vehicles if x.get("vin") == vin), None)
|
||||
else:
|
||||
v = vehicles[0] if len(vehicles) == 1 else None
|
||||
return str(v["state"]) if v else None
|
||||
|
||||
Reference in New Issue
Block a user