Tesla Fleet API in Home Assistant: HTTPS-Host, Keys, nginx
Der Wunsch war klein: Ich wollte in Home Assistant sehen, wie voll der Akku ist, und den Ladestrom steuern können. Mehr nicht. Kein Fuhrpark, keine App, kein Geschäftsmodell — ein Auto, ein Haus, ein Schieberegler.
Am Ende dieses “kleinen Wunsches” hatte ich: ein selbst erzeugtes Schlüsselpaar, einen öffentlich erreichbaren Server mit gültigem Let’s-Encrypt-Zertifikat, eine nginx-Konfiguration mit WebSocket-Headern und DNS-Resolver-Tricks — und eine Datei, die unter einem bis aufs Zeichen vorgeschriebenen Pfad ausgeliefert werden muss:
/.well-known/appspecific/com.tesla.3p.public-key.pem
Genau an dieser Zeile scheitern die meisten. Nicht an Home Assistant, nicht am Auto. An einer statischen Datei.
Warum Tesla das überhaupt verlangt
Es hilft ungemein, den Grund zu verstehen, statt Configs blind zu kopieren.
Tesla hat den alten, inoffiziellen Weg (App-Zugangsdaten nachbauen und die interne API ansprechen) zugedreht und durch die Fleet API ersetzt. Darin bist du kein Nutzer mehr, der sich einloggt — du bist ein Drittanbieter-Entwickler mit Identität. Und Fahrzeugbefehle werden kryptografisch signiert, nicht einfach als Klartext-Request abgeschickt.
Damit das funktioniert, braucht Tesla zwei Dinge:
- Eine Domain, die dir gehört. Du trägst sie in der Entwicklerkonsole ein.
- Einen öffentlichen Schlüssel unter dieser Domain, an genau dem Well-Known-Pfad oben, über valides HTTPS abrufbar.
Tesla holt sich diese Datei selbst ab. Das ist gleichzeitig Domain-Eigentumsnachweis und Schlüsselverteilung: Wer die Datei dort platzieren kann, kontrolliert die Domain — und der Schlüssel darin ist der Anker, gegen den signierte Befehle geprüft werden.
Aus Teslas Sicht ist das sauber. Kann man so sehen. Aus Sicht von jemandem, der einfach nur sein eigenes Auto laden möchte, bedeutet es: du betreibst jetzt Infrastruktur. Ein öffentlicher HTTPS-Host mit Zertifikat, damit ich einem Auto, das vor meiner Haustür steht, sagen darf, wie viel Strom es ziehen soll. Absurd ist das richtige Wort dafür.
Tesla ändert an der Fleet API regelmäßig Details — Scope-Namen, Button-Beschriftungen im Portal, welche Aktionen zwingend über den Command-Proxy laufen müssen. Was hier steht, ist mein realer Aufbau. Die exakten Bezeichnungen prüfst du bitte in Teslas aktueller Entwicklerdokumentation, nicht in einem Blogartikel von letztem Monat.
Der Weg in Home Assistant
Der HA-Teil ist der einfachste Teil, und das ist fast schon komisch:
Einstellungen → Geräte & Dienste → Integration hinzufügen → Tesla Fleet
Home Assistant zeigt dir dann die Setup-Anweisungen für Tesla-OAuth bzw. die App-Zugangsdaten. Du brauchst Client-Zugangsdaten aus deiner Tesla-Entwickler-App, die Redirect-URI exakt so, wie HA sie anzeigt, und danach einen Browser-Login mit deinem Tesla-Konto.
Der Entwickler-App-Teil bei Tesla ist, um es freundlich zu sagen, der nervige Teil. Unfreundlich gesagt: Ich bin jetzt „Drittanbieter- Entwickler”, weil ich meinen eigenen Akkustand sehen will. Da liegt die Arbeit, nicht in HA.
Das Schlüsselpaar
Erzeugt wird ein EC-Schlüsselpaar (prime256v1). Der private Schlüssel bleibt bei dir und wird nie ausgeliefert:
# Privater Schlüssel — bleibt geheim, niemals veröffentlichen
openssl ecparam -name prime256v1 -genkey -noout -out tesla-private.pem
# Öffentlicher Schlüssel — genau dieser wird publiziert
openssl ec -in tesla-private.pem -pubout -out com.tesla.3p.public-key.pem
sudo mkdir -p /etc/nginx/certs
sudo cp com.tesla.3p.public-key.pem /etc/nginx/certs/
Der Dateiname ist nicht verhandelbar, und der private Schlüssel gehört
nicht in dasselbe ausgelieferte Verzeichnis. Ich habe ihn bewusst
außerhalb von /etc/nginx/certs liegen.
Die nginx-Konfiguration
Das hier ist mein tatsächlicher Aufbau, nur mit anonymisierten Hostnamen.
Ersetze ha.example.com durch deine öffentliche Domain und
ha-backend.example.com durch deine interne HA-Adresse (bei dir kann das
auch eine feste IP wie 192.168.1.50 sein — meine ist es nicht, deine sieht
mit Sicherheit anders aus).
# Wird auf http-Ebene gebraucht, sonst kennt nginx $connection_upgrade nicht
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# HTTP (80) — ACME erlauben, alles andere auf HTTPS umleiten
server {
listen 80;
listen [::]:80;
server_name ha.example.com;
# Muss auf Port 80 erreichbar BLEIBEN, sonst schlägt jede
# Zertifikatserneuerung still fehl.
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS (443)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ha.example.com;
ssl_certificate /etc/letsencrypt/live/ha.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ha.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header Referrer-Policy no-referrer-when-downgrade;
# Home Assistant Reverse Proxy
location / {
# resolver + Variable = nginx löst den Namen zur LAUFZEIT auf.
# Ohne das cacht nginx die IP beim Start — tödlich hinter DynDNS.
resolver 1.1.1.1 8.8.8.8 valid=300s ipv6=off;
set $backend "http://ha-backend.example.com:8123";
proxy_pass $backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Ohne diese beiden Zeilen lädt die HA-Oberfläche nicht richtig
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# HA hält langlebige Verbindungen offen
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
# WICHTIG: eigener location-Block, damit der Proxy oben
# diesen Pfad nicht verschluckt.
location /.well-known/appspecific/com.tesla.3p.public-key.pem {
root /etc/nginx/certs;
}
}
Drei Stellen verdienen Aufmerksamkeit:
Der ACME-Pfad auf Port 80. Verlockend ist, Port 80 komplett umzuleiten. Dann bekommst du beim ersten Mal noch ein Zertifikat und in 90 Tagen keins mehr — und merkst es erst, wenn Tesla die Schlüsseldatei nicht mehr abrufen kann.
Der eigene location-Block für die Tesla-Datei. location / mit
proxy_pass würde die Anfrage sonst an Home Assistant weiterreichen, das
brav 404 antwortet. nginx bevorzugt hier den exakten längeren Präfix — das
ist die halbe Miete.
resolver plus set $backend. nginx löst einen in proxy_pass
hartkodierten Hostnamen genau einmal beim Start auf. Mein HA hängt hinter
DynDNS; wechselt die IP, proxied nginx danach fröhlich ins Leere, bis jemand
neu lädt. Über eine Variable zwingt man nginx zur Laufzeit-Auflösung. Das ist
kein Zierrat, das ist der Grund, warum der Aufbau nachts nicht stirbt.
Danach:
sudo nginx -t && sudo systemctl reload nginx
curl -sI https://ha.example.com/.well-known/appspecific/com.tesla.3p.public-key.pem
Erwartet wird ein 200 OK. Alles andere löst du jetzt und nicht später
im Tesla-Portal.
Fehlersuche
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Schlüssel-URL liefert 404 | location / schluckt den Pfad |
Eigenen location-Block für die PEM-Datei anlegen, nginx -t, reload |
| Schlüssel-URL liefert HA-Login | Gleiches Problem, Proxy greift zuerst | Wie oben; Block muss im 443-Server stehen |
| Tesla akzeptiert Domain nicht | Zertifikat fehlt/abgelaufen oder nur HTTP | Let’s Encrypt einrichten, Renewal testen |
| Zertifikat wird nie ausgestellt | ACME-Pfad wird auf HTTPS umgeleitet | /.well-known/acme-challenge/ auf Port 80 freihalten |
| Datei lädt im Browser als Download | Content-Type unpassend gesetzt | Meist unkritisch; nicht überschreiben, wenn Tesla sie abholt |
| OAuth schlägt beim Login fehl | Falsche Tesla-Region bzw. falsches Konto | Konto der richtigen Region verwenden, App dort anlegen |
| Login bleibt hängen | 2FA nicht abgeschlossen | Zweiten Faktor im Flow wirklich bestätigen |
| Entitäten da, Befehle scheitern | Fehlende Fleet-API-Berechtigungen/Scopes | Scopes in der Entwickler-App prüfen, neu autorisieren |
| Redirect-Fehler | Redirect-URI weicht ab | Exakt den Wert aus HA übernehmen, zeichengenau |
| HA-Oberfläche lädt halb | WebSocket-Header fehlen | Upgrade/Connection setzen, map-Block ergänzen |
| Nach IP-Wechsel tote Verbindung | nginx hat DNS gecacht | resolver + set $backend verwenden |
Was NICHT funktioniert / was mich Zeit gekostet hat
- Self-signed Zertifikat. Tesla holt die Datei mit einem normalen, prüfenden Client. Ohne öffentlich vertrauenswürdige Kette: keine Chance.
- Nur eine IP statt Domain. Der Pfad muss unter einer Domain liegen, die du in der Konsole eingetragen hast.
- Datei im HA-Verzeichnis ablegen und hoffen. Home Assistant liefert
/.well-known/nicht so aus, wie du es dir wünschst. Der Webserver davor ist der richtige Ort. - Cloudflare-Tunnel oder ähnliche Zwischenschichten blind einwerfen. Kann
klappen — aber jede Ebene, die Header oder Pfade umschreibt, ist eine
weitere Stelle, an der die Datei nicht mehr sauber ankommt. Prüfe von außen
mit
curl, nicht aus dem eigenen Netz. - Trailing Slashes und Groß-/Kleinschreibung. Der Pfad ist exakt.
com.tesla.3p.public-key.pem, sonst nichts. - Zu früh im Tesla-Portal klicken. Erst
curlvon außen grün, dann Portal. Andersherum suchst du den Fehler an der falschen Stelle.
Sicherheit: bitte einmal wirklich nachdenken
Hier hört der Spaß auf: Mit diesem Aufbau steht deine Home-Assistant-Instanz im offenen Internet. Das ist eine ganz andere Risikoklasse als ein Gerät im LAN.
- Starke, einzigartige Passwörter und 2FA in HA aktivieren.
- HA und nginx aktuell halten. Automatisiert, nicht “wenn ich dran denke”.
- Fail2ban oder Rate-Limiting davor ist keine Paranoia.
- Und der wichtigste Punkt: Öffentlich sein MUSS nur die statische PEM-Datei. Sonst nichts. Du kannst genauso gut einen minimalen nginx-Host betreiben, der ausschließlich diesen einen Pfad ausliefert und für alles andere 404 zurückgibt — während Home Assistant selbst hinter VPN oder Tailscale bleibt. Ich habe den vollen Reverse Proxy, weil ich Fernzugriff ohnehin wollte. Wenn du ihn nicht willst, bau ihn nicht.
Fazit
Das ist eine Menge Infrastruktur für ein Auto. Schlüsselpaar, Domain, Zertifikat, Reverse Proxy, Entwicklerkonto — für einen Schieberegler und einen Ladezustand. Und ob der Schieberegler nächstes Jahr noch funktioniert, entscheidet nicht mein Server, sondern Tesla.
Selbst hosten lohnt sich, wenn du ohnehin einen Server mit öffentlicher Domain betreibst, nginx und Zertifikate für dich Alltag sind und du keine Lust auf einen weiteren Cloud-Dienst zwischen dir und deinem Auto hast. Dann sind das zwei Stunden, danach läuft es.
Selbst hosten lohnt sich nicht, wenn dieser Artikel dein erster Kontakt
mit nginx -t ist. Dann nimm einen gemanagten Dienst: Teslemetry und
Tessie übernehmen genau diesen Teil für dich, beide sind
kostenpflichtige Abos, beide sind in Home Assistant gut integriert. Auch die
Community-Integration Tesla Custom und der lokale Weg über Tesla BLE
existieren — BLE ist reizvoll, weil es ohne Cloud auskommt, aber es
funktioniert nur in Bluetooth-Reichweite und ist ein eigenes Projekt für
sich. Welche Variante heute welche Fahrzeuge und Befehle unterstützt, ändert
sich schnell; prüf das vor der Entscheidung.
Was ich mitgenommen habe: Der schwierige Teil dieser Integration ist nicht Home Assistant und nicht Tesla. Es ist eine statische Datei, die an genau einer Stelle liegen muss — und die Erkenntnis, wie viele Wege es gibt, diese eine Datei zu verpassen.