Tesla Fleet API med Home Assistant
Det meste av friksjonen ved å få en Tesla inn i Home Assistant har ingenting med Tesla å gjøre. Det er den moderne Fleet API-ens utkaster i døra: et offentlig-nøkkel-endepunkt domenet ditt må servere før Tesla pairer bilen med noe du kontrollerer. Den offisielle dokumentasjonen vinker mot det. Diskusjonsforumene på nettet svinger mellom «det funker for meg» og atten-stegs-guider. Dette er hva jeg endte opp med, og hva hver del faktisk gjør.
Hva Fleet API faktisk krever
Det forrige owner API-et var utypet, udokumentert og tolerert. Du genererte en token, satte opp tesla_custom HACS-integrasjonen, og bilen dukket opp. Det funket fordi Tesla stort sett ignorerte gapet.
Fleet API erstattet dette med noe strengere. Det er dokumentert, det er typet, det returnerer ordentlige feilmeldinger, og det krever at domenet ditt — det du skal bruke til å snakke med det — publiserer en offentlig nøkkel på https://<ditt-domene>/.well-known/appspecific/com.tesla.3p.public-key.pem. Bilen pairer mot domenet ved å gå den stien og lese nøkkelen. Uten filen, ingen paring. Med en malformert PEM, ingen paring. Med riktig PEM på feil sti, ingen paring.
For en hjemmelab-bruker er dette den klønete delen. Du vil ikke eksponere Home Assistant-instansen din direkte mot internett bare for å servere én fil. Du burde ikke måtte installere en revers-proxy og SSL bare for en PEM. Og du burde definitivt ikke betale for statisk hosting et eller annet sted når filen er et par hundre byte.
Cloudflare Worker-en som løser problemet
ha.khanikar.com kjører allerede gjennom en Cloudflare Tunnel. DNS-en er hos Cloudflare. Det samme er sertifikatet. En Cloudflare Worker er det letteste mulige svaret — en JavaScript-handler på kanten som svarer på én sti:
export default {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/.well-known/appspecific/com.tesla.3p.public-key.pem') {
const pem = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmiX8CtfMnAV7upAGY5iaQ+edDkJa
AOiwlIfCWLw1g1nV/Z4xbj5RY+mz2pYk12GXu0jMmNygedPQ3OZOhurlpg==
-----END PUBLIC KEY-----`;
return new Response(pem, {
headers: {
'Content-Type': 'application/x-pem-file',
'Cache-Control': 'public, max-age=86400',
},
});
}
return fetch(request);
},
};
Alt som ikke er den magiske stien faller gjennom til origin. PEM-en blir cachet på kanten i ett døgn. Cloudflares gratis Worker-nivå er mer enn dette endepunktet noen gang vil bruke.
Den private nøkkelen ligger i passordbehandleren min. Jeg genererte paret lokalt med openssl ecparam -name prime256v1 -genkey -noout -out tesla_private.pem og openssl ec -in tesla_private.pem -pubout -out tesla_public.pem, og limte den offentlige halvdelen inn i Worker-en. Fleet API-ens autentiseringsflyt bruker så den matchende private nøkkelen til å signere virtual-key-forespørsler — bilen stoler bare på kommandoer fra noen som kan signere med nøkkelen den cachet under paringen.
Dashbordet jeg endte opp med

Vehicle Status — batteri, rekkevidde, plassering, klima, dører, sentry, alt på ett kort. Visningen jeg lander på oftest.
Fleet API eksponerer en Tesla Model Y som omtrent femti entiteter gjennom den offisielle Tesla Fleet HACS-integrasjonen. Det meste er støy — interne flagg, debug-tellere, felt ingen har rørt på et år — men de nyttige klumper seg sammen i en håndfull visninger som viste seg å være måten jeg faktisk ser på bilen.




Det hele er Lovelace sections-oppsett med Mushroom-kort, noe som gjør dashbordet responsivt på telefonen uten at jeg trenger å tenke på det. "Drive & Maintenance"-visningen lenger ned bygges av et lite Python-skript, build_tesla_view.py, som leser dashbord-JSON-en, legger til en programmatisk konstruert visning (med betingede maler for fargebånd på dekktrykk og giringstand-labels), og dytter det tilbake via Home Assistants WebSocket-API. Å holde den visningen i kode gjør det enkelt å bygge den på nytt etter at entitetsnavn driver mellom Home Assistant-versjoner.
Å telle bare ladingen jeg betaler for

Drive & Maintenance — visningen bygget fra build_tesla_view.py. Kilometer per måned og år, dekktrykk med grønne/gule/røde bånd, hjemmeladings-totaler, live kjørestatus.
Tesla-integrasjonen eksponerer en livstidsteller charge_energy_added, men den teller alt — Supercharging på langtur, vennens wallbox du lånte en gang, destinasjonsladeren på hotellet. Ingenting av det dukker opp på strømregningen min. For «hva koster det å kjøre denne bilen» trengte jeg en hjemme-bare-måler.
En liten Home Assistant-pakke, packages/tesla_meters.yaml, håndterer dette. Kjernen er en trigger-basert template-sensor som fyrer av når en ladesesjon ender:
- trigger:
- trigger: state
entity_id: sensor.tesla_modely_ppgr_charging
to:
- stopped
- complete
- disconnected
sensor:
- name: "Tesla home charging lifetime"
unit_of_measurement: kWh
device_class: energy
state_class: total_increasing
state: >
{% set prev = this.state | float(0) %}
{% set added = states('sensor.tesla_modely_ppgr_charge_energy_added') | float(0) %}
{% set location = states('device_tracker.tesla_modely_ppgr_location') %}
{% if location == 'home' and added > 0 %}
{{ (prev + added) | round(3) }}
{% else %}
{{ prev }}
{% endif %}
To filtre gjør det egentlige arbeidet. location == 'home' er det åpenbare. added > 0-sjekken betyr noe fordi en "disconnected"-hendelse med null kWh fyrer av når kabelen er tilkoblet men sesjonen aldri startet — lett å overse, og det ville blåst opp tellingen med nøyaktig ingenting hver gang, noe som på et eller annet vis føles verre enn å få et direkte feil tall.
Månedlige og årlige sykluser er nyttemålere lagt over, så tesla_home_charging_monthly og tesla_home_charging_yearly resetter seg selv på plan uten mer kode. Dashbordet leser dem direkte.
Kostnad som ikke lyver for seg selv
Å multiplisere hjemmeladings-kWh med ett enkelt prispunkt ved sesjonsslutt gir deg et tall, men tallet er feil så snart spotprisen beveger seg under sesjonen — noe som på Nord Pool skjer hver time. Den renere tilnærmingen er å integrere over sesjonen.
Det er dette automatiseringen i tesla_meters.yaml gjør. Den fyrer av ved hver endring i sensor.tesla_modely_ppgr_charge_energy_added og legger til (ny − forrige) × gjeldende_spotpris til en persistent akkumulator (input_number.tesla_charging_cost_acc). Tre vakter: bare positivt delta (sesjonsstart-resetter får telleren til å falle til null, noe som ellers ville gitt negativ kostnad), bare hjem-plassering, bare gyldig Nord Pool-pris.
- id: tesla_charging_cost_accumulate
triggers:
- trigger: state
entity_id: sensor.tesla_modely_ppgr_charge_energy_added
conditions:
- condition: template
value_template: >-
{% set f = trigger.from_state.state | float(-1) %}
{% set t = trigger.to_state.state | float(-1) %}
{{ f >= 0 and t >= 0 and (t - f) > 0 }}
- condition: state
entity_id: device_tracker.tesla_modely_ppgr_location
state: home
- condition: numeric_state
entity_id: sensor.electricity_price_current_total
above: 0
actions:
- action: input_number.set_value
target:
entity_id: input_number.tesla_charging_cost_acc
data:
value: >-
{% set prev = states('input_number.tesla_charging_cost_acc') | float(0) %}
{% set delta = (trigger.to_state.state | float(0)) - (trigger.from_state.state | float(0)) %}
{% set price = states('sensor.electricity_price_current_total') | float(0) %}
{{ (prev + delta * price) | round(2) }}
mode: queued
Noen detaljer fortjente plassen sin på den harde måten. mode: queued betyr noe fordi integrasjonen sender ut raske byger av charge_energy_added-økninger når bilen rampes opp — å miste dem ville stille undertelle kostnad. Jeg brukte integrasjonens egen kWh-teller heller enn å avlede fra charger_power, fordi charger_power runder til heltall og fasedeteksjons-logikken av og til flakker mellom verdier som ikke matcher det bilen registrerer. Akkumulatoren er et input_number heller enn en template-sensor så den overlever omstart og kan settes direkte for tilbakefylling — det finnes ingen tilsvarende rømningsvei på en trigger-template.
Resultatet er tesla_home_charging_cost_lifetime og matchende månedlige og årlige nyttemålere. Dashbordet viser siste sesjons kostnad på 2,24 NOK for 1,52 kWh — en kort topp-opp på en lavtariff-time. Lite tall, riktig lite tall.
Fleet API-integrasjonen tok lengre tid å sette opp enn jeg ventet og mindre tid enn den burde ha tatt. Problemet med det offentlige nøkkelendepunktet er genuint forvirrende og de fleste skriveriene på nettet har ikke fått det med seg. Worker-en løste det på et kvarter. Resten — entitetsmapping, dashbord, pakke — var et par kvelder.
Hva det ga: et ærlig svar på «hvor mye koster denne bilen meg hver måned», i ekte penger på min faktiske strømtariff, i stedet for en vag følelse om at elbiler er billige å kjøre. Og et dashbord jeg sjekker ofte nok til at OEM-appen ligger ubrukt i en mappe.
Hvis jeg startet på nytt med en annen elbil, ville jeg bygd kostnads-akkumulatoren først og dashbordet sist. Dashbordet er den synlige tingen, men måleren er den som forteller deg om bilen gjør det du kjøpte den for.