BMW API

Dass mein BMW 225xe per Connected Drive über das BMW Backend angesprochen werden kann, habe ich schon beschrieben. Auf dieser Seite fasse ich meine Analysen und Ergebnisse zusammen, diese Daten auch automatisiert für mein eigenes Monitoring zu erfassen. Die Connected Drive App muss natürlich mit einem WebServer von BMW sprechen. Da habe ich mich natürlich gefragt, ob ich das nicht auch anderweitig ansprechen kann, z.B. von PRTG.

Mittlerweile habe ich meine eigene PowerShell Entwicklung eingestellt, denn es gibt mit "bimmerconnected" eine Python-Implementierung, die sehr einfach zu nutzen ist. Ich habe Sie weiter unten kurz beschrieben. Ich lasse meine Beschreibung meiner Schritte hier dennoch stehen, auch wenn sie vermutlich veraltet ist.

Offizielle Dokumentation

Ehe ich einen Debugger oder Fiddler anwerfe, schaue ich natürlich nach Quellen im Internet, die vielleicht schon etwas dazu hergeben. Mit "BMW CARDATA" hat BMW anscheinend eine entsprechende Schnittstelle gebaut. Ich konnte dazu aber keine Details finden und denke, dass hier wieder kommerzielle Firmen mit BMW über Verträge einen Zugang bekommen.

Das Fahrzeug erzeugt Daten. Das sind zum Beispiel Zustandsdaten wie der Kilometerstand, nutzungsbasierte Daten wie der durchschnittliche Kraftstoffverbrauch oder Ereignis-Daten wie ein automatischer Service-Call. Ein Teil davon wird in Form von sogenannten Telematik-Daten verschlüsselt über die fest eingebaute SIM-Karte ausschließlich an sichere BMW Server übertragen. Von dort aus können Service-Anbieter nach Einwilligung des Kunden diejenigen Daten verschlüsselt beziehen, die sie für bestimmte Dienstleistungen benötigen.
Quelle: https://www.press.bmwgroup.com/deutschland/article/detail/T0271366DE/bmw-group-startet-bmw-cardata:-neue-und-innovative-services-fuer-den-kunden-%E2%80%93-sicher-und-transparent?language=de

Allerdings bin ich ziemlich sicher, dass ich als Einzelperson nicht die Hürden nehmen kann, als "Anbieter" von Diensten aufzutreten. Aber es gibt von BMW ja sowohl einen Zugang per Browser als auch per App (IOS und Android). Und da war meine Neugierde geweckt mit Fiddler genauer hinter die Kulissen zu schauen. Denn die Webseiten von BMW für solche Projekte sind mittlerweile offline.

API erforschen - Browser und Fiddler

Ich bin natürlich nicht der erste Anwender, der einen Blick hinter die Technik werfen will. So gibt es schon einige Projekte und Seiten, die Zusammenhänge beschreiben. Diese beziehen sich zwar primär auf den BMW i3 aber es gibt da doch viele Parallelen

Ich habe natürlich auch erst mal versucht, die Kommunikation der App zu belauschen. Von BMW gibt es nämlich auf eine App fpr IOS und Android und da ist dann kein Browser beteiligt. Leider ist es mir nicht gelungen, die IOS-App über Fiddler zur Funktion zu bringen. Der SSL-Handshake wurde noch gestartet aber dann stand einfach nur ein "Abgebrochen" auf dem Smartphone. Anscheinend nutzt BMW bei den Apps mittlerweile ein Zertifikat-Pinning und damit mein "Self Signed" Fiddler-Zertifikat verschmäht. Es war nämlich schon als "Trusted Root" addiert und im IOS-Browser gab es keine Fehlermeldung beim Zugriff auf die gleiche URL. Dennoch hat die App sich geweigert zu arbeiten.

Interessant war aber, dass ich die Anmeldung dann ohne Proxy durchführen konnte und danach über Fiddler die App weiter belauschen konnte. Als wenn der starke Schutz nur bei der Anmeldung aktiv wäre. Allerdings ist dann natürlich nicht mehr viel passiert, denn die App liest die Daten am Anfang einmal ein und ist damit zufrieden. Andere dekompilieren die App z.B. auf Android oder nutzen einen Paket Interceptor:

Daher habe ich mir den "normalen" Webzugang etwas genauer angeschaut, der unter https://www.bmw-connecteddrive.de erreichbar ist. Da kann ich mich ja auch "anmelden und Details zu "meinem Fahrzeug betrachten. Ich lege nun nicht den gesamten Trace vom Dezember 2017 offen, aber man kann gut erkennen, dass normale HTTP-Requests abgesendet werden.  und "BEARER" als Authentifizierung genutzt wird . Bilder werden von einem anderen Server herunter geladen und die Aufbereitung der Anzeige im Browser erfolgt wohl per JavaScript, welches im Hintergrund über JSON-Anfragen die Daten nachlädt.

Die Anmeldung läuft klassisch über ein FORM POST auf https://customer.bmwgroup.com/gcdm/oauth/authenticate mit Username, Kennwort und einigen weiteren Parametern:

In der folgen kommen viele Abfragen, die mit noch mehr JSON-Daten beantwortet werden bis ich eine URL gefunden habe, die vielversprechend aussieht. Die unkenntlich gemachten Daten enthalten die Fahrgestellnummer (VIM).

Das sah schon mal vielversprechend aus. Nachdem ich ein paar der Werte in Google gesucht habe, habe ich auch Seiten gefunden, die sich mit der APP für den BMW i3 auseinandersetzen und sehr ähnlich aussehen.

Anscheinend hat sich mittlerweile die URL geändert
Alt: https://www.bmw-connecteddrive.com/api/vehicle
Neu: https://b2vapi.bmwgroup.com/api/vehicle 

Ich denke dass alle Fahrzeuge die gleiche Infrastruktur nutzen und entsprechend auch die APIs vergleichbar sind.

Erster Versuch per Powershell

Ich habe mir aus einem Mitschnitt einfach mal den "BEARER"-Code geschnappt und per PowerShell auf die Spur geschickt und auch ohne alle anderen Felder drum herum habe ich Daten bekommen.

$result= Invoke-RestMethod `
   -Method GET `
   -Uri https://www.bmw-connecteddrive.de/api/vehicle/dynamic/v1/WBA2C71000xxxxxxx?offset=-60 `
   -Headers @{Authorization="Bearer xxxxxxxxxxxxxx"}

$result.attributemap

updateTime                                       : 19.12.2017 22:23:11 UTC
updateTime_converted                             : 19.12.2017 21:51
shdStatusUnified                                 : CLOSED
door_lock_state                                  : SECURED
vehicle_tracking                                 : 1
Segment_LastTrip_time_segment_end_formatted_time : 20:50
door_passenger_front                             : CLOSED
check_control_messages                           :
battery_size_max                                 : 7700
beRemainingRangeFuelKm                           : 205.0
beMaxRangeElectricMile                           : 18.0
beMaxRangeElectricKm                             : 29.0
beMaxRangeElectric                               : 29.0
beRemainingRangeElectricMile                     : 14.0
beRemainingRangeElectricKm                       : 22.0
chargingHVStatus                                 : CHARGING
chargingLevelHv                                  : 80.0
chargingLogicCurrentlyActive                     : DIRECT_CHARGING
chargingTimeRemaining                            : 65.0
lastChargingEndReason                            : UNKNOWN
lights_parking                                   : OFF
connectorStatus                                  : CONNECTED
kombi_current_remaining_range_fuel               : 197.0
soc_hv_percent                                   : 55.9
window_passenger_front                           : CLOSED
...

Interessant wären natürlich Batteriedaten, z.B. Gesamtkapazität und aktuell noch nutzbare Kapazität (SOC = State of Charge = Ladestand). In verschiedenen Foren liest man von einem Counter "socMax", der die nutzbare Kapazität der Batterie liefert. Anscheinend verbaut BMW etwas größere Batterien als angegeben, damit diese im Laufe der Alterung immer noch die garantierte Mindestkapazität vorweisen.

Ich habe natürlich auch mal versucht direkt eine "Basic Auth" zu übersenden. Das wurde aber auch mit einem 401 abgelehnt.

So komme ich zu meinem Bearer-Token

Also bleibt nun nur noch der Prozess zu klären, wie ich mit der Eingabe von Benutzername und Kennwort ein gültiges Token generieren kann. Ich habe nochmal die Anmeldung gemacht und folgendes scheint zu funktionieren.

  1. POST https://customer.bmwgroup.com/gcdm/oauth/authenticate
    Übergeben werden dabei u.a. der Benutzername und das Kennwort und dass ein Token angefordert wird.
  2. Die Antwort enthält ein Access-Token
    Das ist etwas weit hinten aber im Location-String zu sehen:
    HTTP/1.1 302 Found
    Date: Tue, 19 Dec 2017 21:48:14 GMT
    Server: Apache
    Max-Forwards: 20
    Via: 1.0 lpb2vcn02 (BMW Group API Gateway)
    Location: https://www.bmw-connecteddrive.com/app/default/static/external-dispatch.html#
                      state=xxxxxx&access_token=BQaQSqe7YQHWQsJCs8MAoL1sVo17U6aa&token_type=Bearer&expires_in=7199
    Content-Type: text/html; charset="utf-8"
  3. Access-Token in die Anfrage übernehmen
    Dann muss ich nur noch den Wert übernehmen und in die oben schon dokumentierte Anfrage einfügen
  4. Antwort auswerten
    Aus der Rückantwort kann ich dann einfach die Daten extrahieren.

Und das Access-Token reicht mir, um die Daten abzufragen. Es läuft wohl in 7200 Sekunden (120Min) ab. Das ist lange genug um einige Zeit lang die Abfragen zu machen. Wenn es abläuft, dann muss der Code mit dem 401 umgehen und ein neues Ticket anfordern. Bei der Analyse der IOS-App habe ich gesehen, dass dies bei einem abgelaufenen Bearer-Token eine "Verlängerung" beantragt, was vermutlich einfacher und schneller möglich sein sollte. Bei Batterie-Geräten ist das sicher wünschenswert. Aber alle 2h sich neu anmelden ist für einen Überwachungsprozess sicher in Ordnung.

Hier habe ich bislang nicht weiter gemacht und ich vermute, dass die API mittlerweile auch weiter entwickelt wurde oder unter einer neuen URL erreichbar ist.

Beifang der App

 Per Browser werden anscheinend Zählpixel von Usabilly.com (Niederlande) und Tracking-JavaScript von bmwag.d3.sc.omtrc.net (Adobe ?) nachgeladen. Die IOS-App scheint regelmäßig an Microsofts Application Insights-Service unter https://dc.services.visualstudio.com/v2/track ein paar Daten zu berichten.

Aber dass er anscheinend jeden Tastendruck meldet finde ich schon etwas bedenklich

Die Funktion kann man aber in den Einstellungen der App auch selbst deaktivieren.

bimmer_connected

Aber nun zu einem Python-Modul, welches die BMW-API abfragt und auch im Mai 2023 problemlos funktioniert hat. Ich brauche auf meinem PC natürlich erst einmal Python. Sie können Python direkt im Internet herunterladen oder sogar über den Windows AppStore installieren. Versuchen Sie einfach "Python" zu starten und Windows springt zum Appstore.

Python Download
https://www.python.org/downloads/

Dann müssen Sie mit dem Python Installer "PIP" nur noch das Modul installieren, z.B.: mit

C:> pip install --upgrade bimmer_connected

PIP hat auch entsprechende Abhängigkeiten direkt mit installiert:

Ich kann nun Python starten und interaktiv in der Shell erste Schritte gehen, z.B. mit:

C:\temp>py
Python 3.11.2 (tags/v3.11.2:878ead1, Feb  7 2023, 16:38:35) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import asyncio
>>> from bimmer_connected.account import MyBMWAccount
>>> from bimmer_connected.api.regions import Regions
>>> account = MyBMWAccount("bmwportal-username", "bmw-Kennwort", Regions.REST_OF_WORLD)
>>> asyncio.run(account.get_vehicles())
>>> vehicle = account.get_vehicle("WBA2xxxxxxxxxxx")
>>> print(vehicle.brand, vehicle.name, vehicle.vin)
CarBrands.BMW 225xe iPerformance WBA2xxxxxxxxxx
>>> print(vehicle.mileage)
ValueWithUnit(value=85266, unit='km')
>>> print(vehicle.has_electric_drivetrain)
True

Sie müssen natürlich hier schon ihre eigenen Zugangsdaten und die Fahrgestellnummer eingeben. Allerdings können Sie "bimmer_connected" auch direkt aus der CMD-Shell starten, wenn Python im Suchpfad ist.

C:> bimmerconnected status "username" "BMWkennwort" rest_of_world

Das geht natürlich auch per PowerShell. Allerdings enthält die Variable dann leider nicht nur die JSON-Ausgabe sondern noch etwas Fülltext am Anfang, wenn Sie nicht den Parameter "--json" addieren.

$bmwdata= bimmerconnected status "username" "kennwort" rest_of_world --json
($bmwdata | Convertfrom-Json)

Mit der Konvertierung nach JSON können Sie natürlich sehr einfach die Daten weiter verarbeiten.

Leider liefert die API keine weiteren Details zur Batterie selbst. Dass sie am Ende der Ladung auf 100% steht, verwundert nicht und die Reichweite und Füllstand mit Benzin ist nett aber keiner Überwachung mit PRTG o.ä. wert.

PS C:\> ($bmwdata | Convertfrom-Json).fuel_and_battery

remaining_range_fuel : {155, km}
remaining_range_electric : {20, km}
remaining_range_total : {175, km}
remaining_fuel : {13, L}
remaining_fuel_percent :
remaining_battery_percent : 100
charging_status : FINISHED_FULLY_CHARGED
charging_start_time_no_tz :
charging_end_time :
is_charger_connected : True
charging_target : 100
account_timezone : Mitteleuropäische Sommerzeit
charging_start_time :

Ich habe aber auch schon APIs gesehen, die einen SoC liefern. Das kann natürlich auch vom Fahrzeug abhängen.

Weitere Links