Files
ems/docs/tesla-fleet-api.md
Dusan Vojacek 2122fa2035
All checks were successful
CI and deploy / migration-check (push) Successful in 47s
CI and deploy / deploy (push) Has been skipped
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>
2026-06-12 14:14:48 +02:00

5.8 KiB
Raw Blame History

Tesla Fleet API — napojení EMS (čtení SoC vozidla)

REDIRECT URI registrovaná v dev portálu: https://ems.vojacek.eu/t-auth (musí se znak po znaku shodovat v authorize URL, v token výměně i v portálu). Stránka s kódem se servíruje na /t-auth (Caddy rewrite); kód jde vždy zkopírovat i z adresního řádku (?code=...).

Cíl: po příjezdu EV přečíst skutečné SoC → ev_session.energy_needed_wh přesně místo defaultu. Doména ems.vojacek.eu slouží JEN jako veřejná vizitka pro Tesla (cert + public key + jednorázový OAuth callback) — EMS samotné zůstává na VPN.

1. Doména (jednorázově, na serveru)

bash /opt/ems-deploy/app/deploy/tesla/setup_tesla_domain.sh
# → přidat vypsaný blok do /etc/caddy/Caddyfile a `systemctl reload caddy`

Veřejné je pouze:

  • https://ems.vojacek.eu/.well-known/appspecific/com.tesla.3p.public-key.pem
  • https://ems.vojacek.eu/t-auth (statická stránka zobrazí ?code=)
  • vše ostatní → 404; certifikát řeší Caddy (Let's Encrypt) automaticky.

2. Tesla developer portál (developer.tesla.com)

Vytvořit aplikaci:

  • Allowed Origin: https://ems.vojacek.eu
  • Allowed Redirect URI: https://ems.vojacek.eu/t-auth
  • Scopes: openid offline_access vehicle_device_data (čtení SoC stačí; vehicle_charging_cmds až kdybychom chtěli vozidlu poroučet my — teď řídí wallbox, ne auto)

→ získáš CLIENT_ID a CLIENT_SECRET.

3. Partner account registrace (jednorázově)

# partner token (client_credentials):
curl -s https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token \
  -d grant_type=client_credentials -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d scope='openid vehicle_device_data' \
  -d audience=https://fleet-api.prd.eu.vn.cloud.tesla.com | jq -r .access_token

# registrace domény (ověří si public key na .well-known):
curl -s -X POST https://fleet-api.prd.eu.vn.cloud.tesla.com/api/1/partner_accounts \
  -H "Authorization: Bearer $PARTNER_TOKEN" -H 'Content-Type: application/json' \
  -d '{"domain":"ems.vojacek.eu"}'

4. Uživatelský OAuth (jednorázově, z prohlížeče)

https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/authorize?
  response_type=code&client_id=$CLIENT_ID&
  redirect_uri=https://ems.vojacek.eu/t-auth&
  scope=openid%20offline_access%20vehicle_device_data&state=ems

Přihlásíš se Tesla účtem → redirect na callback stránku → zkopíruješ code → výměna za tokeny:

curl -s https://fleet-auth.prd.vn.cloud.tesla.com/oauth2/v3/token \
  -d grant_type=authorization_code -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET -d code=$CODE \
  -d redirect_uri=https://ems.vojacek.eu/t-auth
# → access_token (krátký) + refresh_token (ULOŽIT — viz krok 5)

5. EMS integrace (plán implementace)

  • env: TESLA_CLIENT_ID, TESLA_CLIENT_SECRET, TESLA_REFRESH_TOKEN (/opt/ems-deploy/.env); refresh token rotuje → po každém refreshi uložit nový (tabulka ems.tesla_token nebo aktualizace .env jobem — rozhodnout).
  • services/tesla_client.py: refresh → access token (cache ~8 h), GET /api/1/vehicles, GET /api/1/vehicles/{id}/vehicle_datacharge_state.battery_level, charge_state.charge_limit_soc.
  • Hook v _on_ev_arrival (telemetry_collector): po detekci příjezdu zavolat API, energy_needed_wh = max(0, (limit level)/100 × battery_capacity_kwh × 1000 / účinnost nabíjení)fn_ev_session_apply_patch; replan už běží.
  • Mapování vozidlo↔wallbox: asset_vehicle.api_type='tesla' + VIN sloupec (doplnit migrací), heuristika „Tesla je na chargeru X" dle default_charger_id.
  • Pozn.: vehicle_data budí auto (vampire drain) — volat jen při příjezdu a max 1× za session.

Stav (2026-06-11 večer)

  • skript + callback + dokumentace v repu
  • uživatel: doména + Caddy + developer portál (CLIENT_ID/SECRET získány)
  • EMS implementace: services/tesla_client.py (refresh s rotací do ems.tesla_token, vehicles → vehicle_data charge_state, 408=spící auto), hook _patch_session_from_tesla v telemetry_collector (po příjezdu, před replanem; selhání neblokuje plán), fn_tesla_arrival_context, fn_ev_session_apply_patch + soc_at_connect_pct, VIN auto-learn, V086 (vin, api_type='tesla' pro tesla-my, tabulka tesla_token)
  • uživatel: partner registrace (§3) + OAuth (§4) → TESLA_CLIENT_ID, TESLA_CLIENT_SECRET, TESLA_REFRESH_TOKEN do /opt/ems-deploy/.env a docker compose -f /opt/ems-deploy/docker-compose.yml --env-file /opt/ems-deploy/.env up -d backend (recreate kvůli env)
  • ověření: po příjezdu Tesly log Tesla SoC -> session … + select soc_at_connect_pct, target_soc_pct from ems.ev_session order by id desc limit 1

Presence watcher (2026-06-12, dev)

Poll ~5 min: GET /vehicles (state, NEBUDÍ) → při online poloha (location_data, vyžaduje scope vehicle_location) → geofence 150 m vs GPS site → ems.ev_presence_obs (V095). Přechod pryč→doma + Disconnected → Discord pobídka „auto doma a nepíchnuté (+aktuální přebytek)“; cooldown 2 h. Data = základ dostupnostní statistiky per DOW×hodina (follow-up: maska ev_connected v plánovači + zreálnění oportunistické hodnoty).

Zákopy z reauth (12. 6.) — ať se neopakují

  1. redirect URI /t-auth (musí sedět všude), 2. refresh token ROTUJE → provozní hodnota v ems.tesla_token (ne .env), 3. po revokaci souhlasu ~10 min výpadek auth, 4. client_not_found = app smazána/nové ID → opravit .env + recreate, 5. public key hash je vázán na app — po smazání app nutná rotace klíče (mv private.pem .old + setup_tesla_domain.sh) před partner registrací,
  2. prázdný seznam vozidel = chybí partner registrace (ne spánek).