Modul für Tesla Model S / 3 / X

Begonnen von swhome, 17 Juli 2017, 12:53:25

Vorheriges Thema - Nächstes Thema

Oliver Vallant

Zitat von: TechnoTron am 01 April 2022, 14:25:24
Das ist korrekt so.
Einfach den Owner Token nehmen und gut ists.
Danke für dein Feedback. Soweit ich gesehen habe liefern die Tesla Token Apps allesamt zwei Tokens, den AccessToken und den RefreshToken. Einen OwnerToken gibt's da nicht - meinst du mit OwnerToken den RefreshToken?
Wenn ja, wo im Modul 49_TeslaConnection ist der RefreshToken anzugeben, ein Attribut gibt es nur für den AccessToken
Wenn nein, mit welcher App kann man einen OwnerToken generieren?
LG Oliver

abc2006

Hatte gestern (ebenfalls in ermangelung eines Owner-Token) den neuen Access-Token gesetzt. Jetzt habe ich die Meldung
2022.04.03 08:19:25.784 5: car status response {"error":"invalid bearer token"}
Vermutlich ist der AccessToken nach seinen 8h abgelaufen
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

TechnoTron

Nach meinem Wissenstand meine ich den OwnerToken (eu-*******). Der sollte auch nie ablaufen.
Das Problem ist, dass ich damals einige Seiten durchforstet habe um fündig zu werden. Dummerweise habe ich mir die Seite nicht notiert.

Ich werde mich die Tage kurz hinsetzen und meinen Verlauf durchsuchen. Eventuell finde ich die Seite nochmal.

Das Käseparadoxon.

Käse hat Löcher.
Je mehr Käse desto mehr Löcher.
Je mehr Löcher desto weniger Käse.

TechnoTron

Update:
Ich konnte leider in meinem Verlauf nicht mehr die Seite finden wo ich den token generieren konnte. Sry
Das Käseparadoxon.

Käse hat Löcher.
Je mehr Käse desto mehr Löcher.
Je mehr Löcher desto weniger Käse.

Oliver Vallant

#365
Habe das geänderte TokenRefresh-Verfahren in den geforken Code des Jaykoert eingearbeitet und würde bitte die beiden Module nun zu testen.

Änderungen im Überblick:
49_TelsaConnection.pm

  • RefrehToken mit einer Tesla Token App generieren und im Attribut "RefreshToken" einfügen.
  • Habe die Stati "connected" und "Login necessary" geändert in "connected" und "disconnected".
  • Entsprechend die Sets in "connect" und "disconnect". Disconnected deaktiviert nun die Verbindung zum Telsa API. Connect verwirft auch nicht automatisch den aktuellen AccessToken, sondern versucht mit dem aktuellen zu verbinden. Somit fungieren die beiden Sets als Ein-/Ausschalter.
  • refreshAccessToken holt nun einen neuen AccessToken mittels angegebenden RefreshToken unanhängig ob dieser bereits abgelaufen ist. Naturgemäß wird weiterhin ein neier AccessToken geholt, bevor dieser ausläuft.

49_TelsaCar.pm

  • Set init führt einen fullUpdate mit Frischdaten durch, egal in welchem Zustand das Fahrzeug ist.

mahil

Hallo Oliver,

funktioniert bei mir prima, vielen Dank für die Anpassung.

Habe in 49_TeslaCar.pm noch das Kommando charge_amps mit aufgenommen, siehe auch https://forum.fhem.de/index.php/topic,74341.msg1206557.html#msg1206557. Da meine Wallbox keine direkte Steuerung unterstützt, benötige ich das um den Ladestrom beim PV Überschussladen zu begrenzen.
Raspberry Pi4; CUL_MAX: MAX! Heizkörperthermostat basic; Zigbee über Deconz ConBee II: Xiaomi (Thermostate, Fenstersensoren, Bewegungsmelder), Ikea (Bewegungsmelder, Repeater, Leuchtkörper); Shelly: Dimmer, Schalter; Tasmota: Steckdosen, Rollladenschalter

Jaykoert

Hallo Oliver, hallo mahil,

vielen Dank für eure Änderungen. Das charge_amps kann ich auch gut gebrauchen.
Ich habe beide Dateien heruntergeladen und sieht auf den ersten Blick gut aus. Ich werde berichten, wenn ich irgendwelche Auffälligkeiten feststelle.

Gruß
Jaykoert

Elektrolurch

#368
Hallo Oliver,

verstehe ich das richtig:

1. Eigentlich wird nicht mehr zwischen "AccessToken" und "RrefreshToken" unterschieden, d.h. um den aktuellen Token zu aktualisieren, muss man diesen  verwenden und erhält einen neuen zurück.
2. Daraus folgt: Läuft der Token ab, hat man Pech gehabt und muss sich anderweitig einen neuen Token für die Startsequenz besorgen. Hierzu hatte ich bislang die App "TeslaToken" verwandt, die nun aber beim Starten eine Fehlermeldung anzeigt... etwas mit ungültigem Login ?ß??? Hiermnach konnte fhem auch nicht mehr auf das Fahrzeug zugreifen. Abhilfe schaffte dann aber über FHEM ein RefreshToken auszzulösen. Mal sehen, in 6 Tagen läuft mein Token ab.
3. Warum speicherst Du den AccesToken als Attribut? Da kann ja "jeder" den Token auslesen. Wäre es nicht sicherer hierzu "getKeyValue" und "setKeyValue" zu verwenden?
Um das Setzen eines Attributes "AccersToken" für den ersten Aufruf zu vermeiden, habe ich dafür einen set - Befehl definiert.

4. In meinem Code steht für die uri ein anderes Kontrukt, als bei Dir. Hat sich die uri geändert?
Ich habe es derzeit noch blocking:

my($err,$data) = HttpUtils_BlockingGet({
    url => "$hash->{api_uri}/oauth/token",
    timeout => 10,
    noshutdown => 1,
httpversion => "1.1",
    data => {
        grant_type => 'refresh_token',
client_id => $hash->{client_id},
client_secret => $hash->{client_secret},
refresh_token => $refreshToken,
}

bei Dir steht für die uri:

  my $param = {
      url         => "$conn->{auth_uri}",
      timeout     => 10,
      noshutdown  => 1,
      httpversion => "1.1",
      hash        => $conn,
      callback    => \&TeslaConnection_RefreshToken_Callback,
      data        => {
          grant_type    => 'refresh_token',
          client_id     => $conn->{client_id},
          refresh_token => $refreshToken
      }


5. Ich habe mir den Call-Back bei Dir für den Refresh-Token angesehen.
Das json, was da zurück kommt, scheint ja nur noch das Feld "AccessToken" zu haben:

        if( $json->{access_token} ) {
          setKeyValue($conn->{NAME}."_accessToken",  $json->{access_token});
          $conn->{STATE}         = "connected";
          $conn->{expires_at}    = round(gettimeofday() + $json->{expires_in}, 0);
          $conn->{accessToken}   = TeslaConnection_TokenInShort($json->{access_token});
          $conn->{refreshed_at}  = strftime("%F %X", localtime(gettimeofday()));
          undef $conn->{lastError};
          undef $conn->{refreshFailCount};
          readingsBeginUpdate($conn);
          readingsBulkUpdate($conn, "tokenExpiry", strftime("%F %X", localtime($conn->{expires_at})));
          readingsBulkUpdate($conn, "state", $conn->{STATE});
          readingsEndUpdate($conn, 1);
          Log3 $name, 4 , "$name got new accessToken: " . TeslaConnection_TokenInShort($json->{access_token});

Da wird m.M.n. das Attribut "RefreshToken" nicht gesetzt.
In der sub TeslaConnection_RefreshToken frägts Du es jedoch ab:

  my $name = $conn->{NAME};

  my $refreshToken = AttrVal($conn->{NAME}, "RefreshToken", "");
  $refreshToken =~ s/ //g;
  if ($refreshToken eq "") {
    Log3 $name, 4, "$name: no refreshToken to get new accessToken";
    readingsBeginUpdate($conn);
    readingsBulkUpdate($conn, "state", "refreshToken missing");
    readingsEndUpdate($conn, 1);
    return undef;
  } else {
      Log3 $name, 4 , "$name current refreshToken: " . TeslaConnection_TokenInShort($refreshToken);
  }



Bei mir steht da noch das speichern des RefreshToken... ich bin verwirrt:

setKeyValue($name."_accessToken",  $json->{access_token});
setKeyValue($name."_refreshToken", $json->{refresh_token});


Mache ich da einen Denkfehler?

a) Um einen Request an die API zu senden, benötige ich einen gültigen "AccesToken".
b) Um den "AccesToken" zu erneuern, benötige ich einen "RefreshToken".
c) Wo kommt der "RefreshToken" her und wo wird dieser in Deinem Code gesetzt?

Elektrolurch

configDB und Windows befreite Zone!

Oliver Vallant

#369
Hallo Elektrolurch,

vielen Dank für dein review.

ad 1) Eigentlich wird nicht mehr zwischen "AccessToken" und "RrefreshToken" unterschieden

Doch sehr wohl. Die Lebensdauer des AccessTokens wurde auf 8h reduziert un die des RefreshTokens auf 6 Wochen.
Tesla hat jetzt folgende Security-Strategie im Access Management des APIs:

  • Die Kunden/Fahrer/User haben einen Account mittels Login/Passwort am Tesla Portal. Tesla hält den Kunden User an Zweifaktor-Autenthifizierung zu verwenden.
  • Mit diesem Account kann über ein neues OAuth 2.0 Gateway ein RefreshToken für die nächsten 6 Wochen erzeugt werden. Um das abzusichern, wurde aber ein Captcha eingeführt. Dh. ein automatisiertes Abholen von neuen RefrehTokens ist ab sofort meines Kentnissstandes zufolge nicht mehr möglich, sodass die User mittels Dritt-Apps wie Tesla Tokens alle 6 Wochen händisch einen RefreshToken generieren werden müssen.
  • Mittels OAuth 2.0 Gateway kann dann in einem automatisierbaren Schritt mittels RefreshToken ein AccessToken generiert werden, welcher dann den Zugriff auf das Tesla API für 8h jeweils ermöglicht.
Priv.Anm.: Ein wenig wird für meinen Geschmack die Security-Strategie ad absurdum geführt, da Tesla den RefreshToken nicht selbst innerhalb der PKI-Infrastruktur auf eigenem Portal anbietet, sondern es durch Dritt-Apps erlaubt, welche die Login/Passwörter der User nach Eingabe ja "verarbeiten" können/müssen. Möchte aber hier niemanden etwas unterstellen.

ad 2) Läuft der Token ab, hat man Pech gehabt
Das ist mir nicht bekannt, RefreshToken wurde meiner für 6 Wochen gültig (erste Frist bisher noch nicht abgelaufen) ausgestellt und AccessToken läuft sicher nach 8h ab.
Eben das derzeit mögliche hab im Modul 49_TeslaConnection.pm ergänzt. User gibt alle 6 Wochen einen gernerieten RefreshToken ein, und die AccessTokens refreshed das Modul.

ad 3) Warum speicherst Du den AccesToken als Attribut?
Das musst du überlesen haben. Nur der RefreshToken wird in einem Attribute gespeichert und dort bei Generierung eines AccessTokens ausgelesen.
Der AccessToken wird in Zeile 193 mit setKeyValue gespeichert:

setKeyValue($conn->{NAME}."_accessToken",  $json->{access_token});

und angezeigt wird der AccessToken nur unter Internals, verkürzt mit ersten und letzten 25 Zeichen.

ad 4) Hat sich die uri geändert
Ja genau. Hab zur ursprünglichen URI für den API Zugriff ein weiteres URI für den OAuth 2.0 Zugriff hinzugefügt.

  $hash->{api_uri}   = "https://owner-api.teslamotors.com";
  $hash->{auth_uri}  = "https://auth.tesla.com/oauth2/v3/token";

Nonblocking wäre irgendwie dienlicher. client_secret gibt es nicht mehr, führt zu einen Fehler, wenn angegeben.

ad 5) nur noch das Feld "AccessToken" zu haben
Ja das ist richtig, hat sich geändert, da OAuth 2.0 Schlüssel-Management nun vom API strikt getrennt abläuft.

ad 5) Da wird m.M.n. das Attribut "RefreshToken" nicht gesetzt. In der sub TeslaConnection_RefreshToken frägts Du es jedoch ab
Wie oben beschrieben, setzt der User den RefrehToken als Attribute und dieser wird bei jedem Refreshment des AccessToken ausgelesen.

ad 5) Bei mir steht da noch das speichern des RefreshCodes... ich bin verwirrt
Früher wurde ja mittels Login/Passwort direkt über das API die Tokens erzeugt und du hast diese mit SetKeyValue temporär verspeichert. Jetzt gibt es nur mehr den temporären AccessToken, der automatisch erzeugt werden kann.

ad a) Um einen Request an die API zu senden, beötige ich einen gültigen "AccesToken".  ....richtig
ad b) Um den "AccesToken" zu erneuern, bernötige ich einen "RefreshToken". ...richtig
ad c) Wo kommt der "RefreshToken" her und wo wird dieser in Deinem Code gesetzt? ... muss leider manuell mit Captcha in einer Dritt-App erstellt und in einem Attribute gespeichert werden

Vielen Dank für deine Mühe,
LG Oliver

Elektrolurch

Hallo Oliver,

vielen Dank für Deine ausführliche Beschreibung.
Daraufhin habe ich mir noch einmal den Code angesehen und verstehe nun, um was es da geht.
Bleiben da noch allerdings einige Fragen:

1. Wenn ich mich auf der Webseite von Tesla auf mein Konto einlooge und auf die Kontoeinstellungen gehe, gibt es dort die Option für MFA.
Derzeit sieht es für mich so aus, als wäre dies noch keine zwingende Option.
Tesla spricht dort von "Authentificator App", ohne eine konkrete zu nennen. Ich habe bislang die "Tesla Token" verwendet, die wurde Anfang März aktualisiert, wohl aber nicht wegen einer geänderten Autenthification.
Wenn ich die Aufrufe, meldet die nun einen Fehler beim login.

a) Welche App verwendest Du?
b) Über die Webseite von Tesla kann man wohl den refreshToken nicht generieren, das schriebst Du ja.

c) Die von Dir vorgenommenen Änderungen: gelten die nur für MFA? Oder gibt es dies zukünftig nicht mehr?

Ich hatte eben beim Aufruf der Webseite -> login einen merkwürdigen Effekt:
Habe Mailadresse und Kennwort eingegeben -> Die Seite wurde mit leerem Passwortfeld und einem Chapka angezeigt.
Nach "Aktualisieren" (F5 des Browsers) war ich jedoch eingeloggt. MFA ist bei mir noch nicht aktiviert.

Mein accessToken ist noch 4 Tage gültig. Wenn ich versuche, ihn zu erneuern, bekomme ich:

2022.04.23 17:20:09 1: teslaconn: RefreshTokenResponse {"error":"unauthorized_client","error_description":"Unauthorized client","error_uri":"https://auth.tesla.com/error/reference/xyz..."}


Da muss es wohl ein refreshCode sein, der über das Chapka und einer "Authentificator App" generiert wurde.

Alle 6 Wochen... na ja - ging ja noch.


Elektrolurch
configDB und Windows befreite Zone!

Oliver Vallant

Hallo Elektrolurch,

ad a)
Ich verwende eine Applikation unter Linux von adriankumpf:
https://github.com/adriankumpf/tesla_auth
Ist dort als bin für Linux/mac/Windows herunterladbar. Angeführt ist, dass es mit MFA auch funktioniert, obschon ich MFA bei meinem Account nicht aktiviert habe und in concreto noch nicht getestet habe.
Sollte aber mit jeder anderen Tesla Token App auch gehen, welche mit dem neuen Verfahren einen gültigen RefreshToken generieren kann.

ad c)
MFA hat mit meinen Ergänzungen nichts zu tun. MFA kommt ja nur beim Einloggen mit Login/Passwort in das Tesla Portal und beim Generieren eines RefrehTokens mittels einer App oder dem unter a) genannten bin zum Einsatz. Alle 6 Wochen wird man mit oder ohne MFA einen RefrehToken leider manuell erstellen müssen und in das gleichnamige Attribut des gegenständlichen Moduls speichern müssen. Ab dann holt sich das Modul für jeweils 8h einen neuen AccessToken.


ad login einen merkwürdigen Effekt)
Klingt merkwürdig. Vielleicht stand dem Browser noch ein gültiges Session-Cookie zur Verfügung und hat es beim Refresh zur auth verwendet?


ad Mein accessToken ist noch 4 Tage gültig)
Der RefreshToken ist nicht gültig.

ad Da muss es wohl ein refreshCode sein)
ja

LG Oliver

Elektrolurch

Hallo Oliver,

Zitat:
MFA hat mit meinen Ergänzungen nichts zu tun.
...

Ok, das hatte ich falsch verstanden....
Diese Aussage hilft mir sehr.
Ich hatte mich schon gewundert, dass die Apps, die es da gibt, keinen Kamera-Modus für ein Chapka haben....

Die App "TeslaToken" liefert mir nun wieder gültige Daten. Der AT ist 8 Stunden nun gültig nach Angabe der App.
Die App hat einen "Playground" damit kann man den Token testen und sieht auch, was da zurück kommt. M3 kann ich mit dem AT aufwecken.
Habe AT und RT nun in fhem hinterlegt, der AT funktioniert dort auch.

Nun bin ich gespannt, ob die Aktualisierung mit Hilfe des RT nun auch funktioniert.

Danke für die Unterstützung....

Elektrolurch
configDB und Windows befreite Zone!

Elektrolurch

Hallo Oliver und Tesla-Gemeinde,

bei mir funktionierte der refresh des Access-Tokens zunächst einmal nicht mehr.
Es hat sich dann foldendes herausgestellt (damit andere nicht auch so lange danach suchen müssen):

Für die client_id, die bei dem Refesh übergeben werden muss, ist zwingend:
"ownerapi"
erforderlich.
Aus älteren Zeiten hatte ich da eine Nummer und auch noch ein secret im Hash des teslaconn - Objektes hinterlegt.

Steht im Request nicht "ownerapi", so erfolgt die Fehlermeldung:
"unauthorized client"

Elektrolurch


configDB und Windows befreite Zone!

Oliver Vallant

Hallo Elektrolurch,

das ist richtig, deshalb in Zeile 105 in 49_TeslaConnection.pm so eingebaut:
$hash->{client_id} = "ownerapi";

LG Oliver