Seite 2 von 30

Re: Mercedes SoC

Verfasst: Di Jul 28, 2020 8:42 pm
von solarjunkie
LutzB hat geschrieben: Di Jul 28, 2020 8:09 am Da hast Du Recht. Schön ist das nicht, aber zumindest eine Möglichkeit.
Hallo zusammen,

hab' da mal weitergeklickt: die Registrierung als Developer sind nur wenige Klicks und man kann den bestehenden "Mercedes Me" account nutzen. Für das eigene Fahrzeug ist die Nutzung kostenlos (BYOV Bring your own vehicle).

Bin nicht besonders fit mit Oauth2, aber ich werde mal versuchen, mich da durchzufummeln. Werde dann berichten.

Nachtrag:
Habe über die API den SoC für mein eigenes Auto erfolgreich abgefragt.
Werde daraus jetzt mal ein Script und ein HowTo bauen und das dann zeitnah hier posten.

VG,
Ralf

Re: Mercedes SoC

Verfasst: Mi Jul 29, 2020 11:36 pm
von solarjunkie
So,

ich habe eine prototypische Lösung, die für mich funktioniert, und die ich hier gern teilen möchte, in der Hoffnung, dass sie dem ein oder anderen nützt. Auf jeden Fall ist sie kostenlos. Unten findet Ihr die Anleitung und meine Scripte. Jetzt werde ich erstmal beobachten, wie sich das im Dauerbetrieb bewährt.

Ich freue mich auf Eure Rückmeldungen und darüber, wie flexibel man die openWB selber erweitern kann!

VG,
Ralf



Voraussetzung:
Mercedes Me Account muss vorhanden sein und ein Fahrzeug muss damit verbunden sein. Dort kann man auch die FIN raussuchen.

Die folgenden Schritte in der Reihenfolge durchführen.

1. https://developer.mercedes-benz.com/
dort mit dem Mercedes Me Account einloggen

2. https://developer.mercedes-benz.com/pro ... cle_status
> Get Access
> Bring Your Own Car (ist kostenlos für das eigene Fahrzeug)
> Package "Standard", man kann eh nichts anderes auswählen --> Next
> Continue to create a new "App" --> Next
> Create Application
> Application Name: Hier kann ein beliebiger Name stehen. Z.B.: "Fritzis E-Auto Abfrage"
> Die anderen Felder kann man leer lassen
> Anschließend findet man unter "Console" folgendes:
> App ID (hier nicht weiter benötigt)
> Client ID
> Client Secret
> Redirect URLs --> Habe ich geändert zu http://localhost/ (mit Slash am Ende, k.A. ob nötig, muss aber später zum Request passen, sonst kommt ein Fehler)

Das war es

Jetzt kommen die Scripte zum Einsatz, diese habe ich alle nach

Code: Alles auswählen

/var/www/html/openWB/modules/soc_mercedes/
kopiert.

a) authorize.py - muss man im Idealfall nur einmal aufrufen. Es "besorgt" den ersten access token und den refresh token. Danach läuft alles automatisch. Wenn mit den Tokens doch mal was schief geht, kann man hiermit immer wieder eine neue OAuth2 Autoriserung starten.

Code: Alles auswählen

# thanks to https://developer.byu.edu/docs/consume-api/use-api/oauth-20/oauth-20-python-sample-code

"""
this file should be called in an interactive shell, as it generates output and expects user input.
It needs to be called only once at the beginning (and in cases where the refresh token has expired)
"""

import requests, json

from myAppDetails import authorize_url, token_url, callback_uri, client_id, client_secret

# simulate a request from a browser on the authorize_url - will return an authorization code after the user is
# prompted for credentials. The code can be found in the address line of the browser
authorization_redirect_url = authorize_url + '?response_type=code&client_id=' + client_id + '&redirect_uri=' + callback_uri + '&scope=mb:vehicle:mbdata:evstatus'

print("go to the following url on the browser and enter the code from the returned url: ")
print("---  " + authorization_redirect_url + "  ---")
authorization_code = input('code: ')

# turn the authorization code into a access token, etc
data = {'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': callback_uri}
print("requesting access token")
access_token_response = requests.post(token_url, data=data, verify=True, allow_redirects=False, auth=(client_id, client_secret))

print("response")
print(access_token_response.headers)
print('body: ' + access_token_response.text)

# we can now use the access_token as much as we want to access protected resources.
# Storing the access token in a file.
tokens = json.loads(access_token_response.text)
access_token = tokens['access_token']
refresh_token = tokens['refresh_token']

outFile = open('access_token','w')
outFile.write(access_token)
outFile.close()

outFile = open('refresh_token','w')
outFile.write(refresh_token)
outFile.close()

print("stored access token: " + access_token)
print("stored refresh token: " + refresh_token)
b) mercedessoc.py - fragt den Ladestand und die Reichweite ab und schreibt die Ergebnisse nach /var/www/html/openWB/ramdisk/mymercedes_soc und mymercedes_range (nur so). Mache ich per cron alle 5 min

Code: Alles auswählen

# thanks to https://developer.byu.edu/docs/consume-api/use-api/oauth-20/oauth-20-python-sample-code
"""
This file should be called in regular intervals, but not too often
to not exceed the budget of free API calls included with the BYOCAR
subscription.
"""

import requests
import json

from myAppDetails import test_api_url, logging

# read currently active access token from disk
inFile = open('access_token','r')
access_token = inFile.read().rstrip()
inFile.close()
logging.debug('read access token from disk: %s' % access_token)

# prepare and execute API call
api_call_headers = {'Authorization': 'Bearer ' + access_token}
api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=True)
logging.debug('received Mercedes API response: %s' % api_call_response.text)

# parse response to extract SOC and Range (not needed for now)
# Example response: [{"soc":{"value":"100","timestamp":1596019475000}},{"rangeelectric":{"value":"37","timestamp":1596019751000}}]
# NOTE: the order may vary. Search for the list entry with
# according to API documentation, response will stay empty and status code 204 when no updated data is available
if api_call_response.status_code not in (200, 204):
    # error logging
    logging.error("Failed to retrieve Mercedes vehicle status. Received:\nStatus Code: %d\nResponse: %s" % api_call_response.text)
    exit(1)
elif api_call_response.status_code ==204:
    # exception,not critical
    if len(api_call_response.text)==0:
        logging.info('Received empty response with status code 204. Vehicle did not provide update for >12h. Keeping soc unchanged.')
        exit(0)
elif api_call_response.status_code == 200:
    # normal parsing of result
    resources = json.loads(api_call_response.text)
    soc=None
    range=None
    for entry in resources:
        if 'soc' in entry.keys():
            soc = entry['soc']['value']
            outFile = open('/var/www/html/openWB/ramdisk/mymercedes_soc', 'w')
            outFile.write(soc)
            outFile.close()
        if 'rangeelectric' in entry.keys():
            range = entry['rangeelectric']['value']
            outFile = open('/var/www/html/openWB/ramdisk/mymercedes_range','w')
            outFile.write(range)
            outFile.close()
    logging.info('Retrieved and stored Mercedes Vehicle Status: SOC '+soc+'%, Range '+range+'km' )
c) refresh.py - erneuert regelmäßig den access_token, damit keine neuerliche user Authentifizierung im Browser nötig wird. Mache ich per cron alle 30 min.

Code: Alles auswählen

# thanks to https://developer.byu.edu/docs/consume-api/use-api/oauth-20/oauth-20-python-sample-code

"""
this file needs to be run to refresh an expired access token
"""

import requests
import json

from myAppDetails import token_url, client_id, client_secret, logging

inFile = open('refresh_token','r')
refresh_token = inFile.read().rstrip()
inFile.close()

# use the refresh token to get a NEW access token
data = {'grant_type': 'refresh_token', 'refresh_token': refresh_token }
logging.debug("requesting updated access token")
refresh_token_response = requests.post(token_url, data=data, verify=True, allow_redirects=False, auth=(client_id, client_secret))

# logging
logging.debug("response")
logging.debug(refresh_token_response.headers)
logging.debug('body: ' + refresh_token_response.text)

# we can now use the NEW access_token as much as we want to access the API. The token expires after 3600s, therefore we need to call the refresh action before that, e.g. every 30mins
tokens = json.loads(refresh_token_response.text)
access_token = tokens['access_token']
refresh_token = tokens['refresh_token']

# store NEW tokens in a file
outFile = open('access_token','w')
outFile.write(access_token)
outFile.close()
outFile = open('refresh_token','w')
outFile.write(refresh_token)
outFile.close()

# more logging
logging.info("stored updated access token: " + access_token)
logging.info("stored updated refresh token: " + refresh_token)
d) myAppDetails.py - enthält gemeinsam genutzte Settings. Hier müssen die Client ID und das Client Secret von oben eingetragen werden, ausserdem die Fahrzeug-Identifikations-Nr (FIN)

Code: Alles auswählen

"""
this files holds the configuration details
"""
# client (application) credentials - you get these when signing up for the service in the section "Console"
client_id = '<<<Your Client ID Here>>>'
client_secret = '<<<Your Client Secret Here>>>'
# this must match the setting made in the "Console"
callback_uri = "https://localhost/"

# Your Vehicle ID
FIN='<<<Your FIN Here>>>'

# required for OAuth2
authorize_url = "https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/authorize"
token_url = "https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/token"

# The actual home of the API
test_api_url = "https://api.mercedes-benz.com/vehicledata/v1/vehicles/"+FIN+"/containers/electricvehicle"

# logger settings for the other scripts
import logging
logging.basicConfig(filename='/var/www/html/openWB/modules/soc_mercedes/app.log',level=logging.DEBUG, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p')
3. Eintragen der persönlichen Settings in d)

4. Einmalige Autorisierung mit script a)

> Das Script ausführen
cd /var/www/html/openWB/modules/soc_mercedes/ && python3 /var/www/html/openWB/modules/soc_mercedes/authorize.py
> Die auf der Konsole angezeigte URL in ein Browserfenster pasten
> Die Mercedes Me Seite sollte erscheinen und ggfs Login sowie Consent zur Weitergabe der Daten an "Fritzis E-Auto Abfrage" abfragen
> Anschließend ruft sie einen callback auf localhost auf, der steht dann in der Browser Adresszeile, da die URL nicht aufgelöst werden kann
> In der Addresszeile des Browsers steht dann sowas wie

Code: Alles auswählen

https://localhost/?code=614d8e2f-2cd0-496f-9fd6-c79b6fa2a1d0
> Der Code ist der Access Grant. Den Code kopieren und an der Eingabeaufforderung auf der Konsole eingeben
> Das Script fragt dann mit dem o.g. Grant den Access Token und den Refresh Token an und speichert diese lokal

5. Scripte b) und c) in die crontab einbinden. Sieht bei mir so aus:

Code: Alles auswählen

1 0 * * * /var/www/html/openWB/runs/cronnightly.sh >> /var/log/openWB.log 2>&1
*/5 * * * * cd /var/www/html/openWB/modules/soc_mercedes/ && python3 /var/www/html/openWB/modules/soc_mercedes/mercedessoc.py && /var/www/html/openWB/runs/cron5min.sh >> /var/log/openWB.log 2>&1
@reboot /var/www/html/openWB/runs/atreboot.sh >> /var/log/openWB.log 2>&1
*/30 * * * * cd /var/www/html/openWB/modules/soc_mercedes/ && python3 /var/www/html/openWB/modules/soc_mercedes/refresh.py
* * * * *  /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
* * * * * sleep 10 && /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
* * * * * sleep 20 && /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
* * * * * sleep 30 && /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
* * * * * sleep 40 && /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
* * * * * sleep 50 && /var/www/html/openWB/regel.sh >> /var/log/openWB.log 2>&1
6. In den openWB Einstellungen unter Modulkonfiguration das SoC HTTP Modul einstellen. URL ist

Code: Alles auswählen

http://localhost/openWB/ramdisk/mymercedes_soc
Das war's! Ich hoffe, dass die Anleitung nachvollziehbar ist.

Die Scripte loggen nach /var/www/html/openWB/modules/soc_mercedes/app.log . Optional kann man noch in myAppDetails.py das loglevel von logging.DEBUG auf logging.INFO umstellen, wenn alles läuft.

PS: sorry für die englischen Kommentare und Ausgaben. Berufskrankheit. Ist mir erst nachher aufgefallen, dass hier alles auf Deutsch ist.

Re: Mercedes SoC

Verfasst: Do Jul 30, 2020 4:11 am
von openWB
Super, Danke!
Ich hoffe wir kriegen das dann auch irgendwann noch Endnutzertauglich hin.

Re: Mercedes SoC

Verfasst: Do Jul 30, 2020 6:41 am
von solarjunkie
Hi,

danke für die Rückmeldung. Prinzipiell wäre es schon möglich, das noch Enduser-tauglicher zu machen.

Ich würde jetzt aber erstmal im Alltag schauen, wie sich das so schlägt. Einen Bug habe ich heute früh schon gefunden und den Code in meinem vorigen Post angepasst.

VG,
Ralf

Re: Mercedes SoC

Verfasst: Do Jul 30, 2020 6:56 am
von LutzB
Für ein richtiges "SoC-Modul" musst Du noch eine Datei "main.sh" erstellen. Die wird von der Regelung im eingestellten Regelintervall aufgerufen und macht Deine Cron-Einträge überflüssig. Als Grundlage könntest Du das Tesla-Modul nehmen, da es eine ähnliche Authentifizierung nutzt. Ich kann Dir auch gerne dabei behilflich sein, kann es jedoch mangels Mercedes und Zugangsdaten nicht testen.

Gruß,
Lutz

Re: Mercedes SoC

Verfasst: Do Jul 30, 2020 7:31 am
von aiole
Klasse Jungs - ich bin immer wieder begeistert zu sehen, was hier abgeht. Open Source-SW war eine der besten Entscheidungen.
VG aiole

Re: Mercedes SoC

Verfasst: Do Jul 30, 2020 8:18 am
von solarjunkie
LutzB hat geschrieben: Do Jul 30, 2020 6:56 am Für ein richtiges "SoC-Modul" musst Du noch eine Datei "main.sh" erstellen. Die wird von der Regelung im eingestellten Regelintervall aufgerufen und macht Deine Cron-Einträge überflüssig. Als Grundlage könntest Du das Tesla-Modul nehmen, da es eine ähnliche Authentifizierung nutzt. Ich kann Dir auch gerne dabei behilflich sein, kann es jedoch mangels Mercedes und Zugangsdaten nicht testen.

Gruß,
Lutz
alles klar. Danke für das Angebot. Werde ich mir bei Gelegenheit mal anschauen. Diese Woche wohl nicht mehr.
VG,
Ralf

Re: Mercedes SoC

Verfasst: Mo Aug 24, 2020 8:13 pm
von solarjunkie
Ein Update hierzu: Nachdem das einen Monat lang klaglos funktioniert hat, kommt von dem Service seit gestern morgen ca. 04:50h ein Fehler.

HTTP Status code 504 und

Code: Alles auswählen

{"fault":{"faultstring":"Gateway Timeout","detail":{"errorcode":"messaging.adaptors.http.flow.GatewayTimeout"}}}
Gleichzeitig ist mir auf der Mercedes Developer Seite (https://developer.mercedes-benz.com/pro ... us/details)
folgendes aufgefallen:

Technical limitations
2 API calls accessible per Daimler vehicle per hour


Bin mir nicht sicher, ob das früher schon da stand. Ich habe jedenfalls einen Monat lang alle 5min den Status abgefragt.

Ich versuche mal, rauszubekommen, was es damit auf sich hat und melde mich wieder.

Re: Mercedes SoC

Verfasst: Do Aug 27, 2020 9:10 pm
von solarjunkie
solarjunkie hat geschrieben: Mo Aug 24, 2020 8:13 pm Ich versuche mal, rauszubekommen, was es damit auf sich hat und melde mich wieder.
Es kam folgende Antwort vom MB /developers Team:

Code: Alles auswählen

Hallo Ralf,
leider haben wir seit ein paar Tagen vermehrt Probleme mit dem API-Service, welches die Daten bereitstellt. Aus diesem Grund kommen vermehrt Fehler zustande.
Wir haben die API-Service-Verantwortlichen bereits darüber informiert und wir warten aktuell auf die Behebung des Incidents.
 Kind regards
Es besteht also Anlass zur Hoffnung.
VG

Re: Mercedes SoC

Verfasst: Do Aug 27, 2020 9:13 pm
von aiole
Da muss man ja froh sein, dass sie nicht 3 Verantwortliche für A + P + I haben :lol:.