# 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
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ě)

```bash
# 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:

```bash
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_data`
  → `charge_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)

- [x] skript + callback + dokumentace v repu
- [x] uživatel: doména + Caddy + developer portál (CLIENT_ID/SECRET získány)
- [x] 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í,
6. prázdný seznam vozidel = chybí partner registrace (ne spánek).
