[Discussie over het verkrijgen van toegang tot de OMNI API afgesplitst naar een eigen topic. Stromeur.]
Dat stromer geen API vrijgeeft betekent opzich niet dat er niks mee te doen is. Met behulp van een stukje software (mitmproxy) kan je het verkeer tussen de app en stromer service afluisteren en dus de functionaliteiten die de app bevat zelf nuttig gebruiken. Als ik begin April mijn Stromer krijg heb ik in ieder geval een leuk projectje.
Toegang krijgen tot de OMNI API
-
- Expert
- Berichten: 102
- Lid geworden op: 23 dec 2017, 21:53
- Locatie: Dordrecht
- Merk SP: Stromer ST3
Vandaag eens in de api van Stromer gedoken. Via deze url loopt de verbinding https://api3.stromer-portal.ch/rapi/mobile/v2/bike/ krijg alleen de authenticatie nog niet goed buiten de app.
Waar ik al wel uit ben is dat er geen actuele gps data via de api te vinden is. De omni stuurt dus niet zo vaak een gps update naar de cloud.
ik kwam wel data tegen die niet in de app terug te vinden is zoals:
"atmospheric_pressure": 10370,
"average_energy_consumption": 109,
"power_on_cycles": 34,
Nummers van onderdelen. Naast de display, controller en motor ook een serie nr van de fork.
Al met al niet heel interessant om verder naar te kijken. De beveiliging ziet er wel goed uit dus wat dat betreft ben ik tevreden
Waar ik al wel uit ben is dat er geen actuele gps data via de api te vinden is. De omni stuurt dus niet zo vaak een gps update naar de cloud.
ik kwam wel data tegen die niet in de app terug te vinden is zoals:
"atmospheric_pressure": 10370,
"average_energy_consumption": 109,
"power_on_cycles": 34,
Nummers van onderdelen. Naast de display, controller en motor ook een serie nr van de fork.
Al met al niet heel interessant om verder naar te kijken. De beveiliging ziet er wel goed uit dus wat dat betreft ben ik tevreden
Stromer ST3 2020 Deep Black
Bodyfloat
Bodyfloat
- Stromeur
- Admin | Forum Supporter
- Berichten: 22959
- Lid geworden op: 15 mei 2017, 20:18
- Locatie: Friesland
- Merk SP: Stromer ST3 PINION SE
Ik kan echt niet wat jij kunt, dus ik ben diep onder de indruk. Ik zou die URL nog niet eens gevonden hebben. Hoe kom ik bijvoorbeeld in de API van mijn eigen Stromer?
Mocht je nog meer van dit soort boeiende informatie tegen komen, shoot!
Mocht je nog meer van dit soort boeiende informatie tegen komen, shoot!
Stromer ST1 X 2017 - Stromer ST2 S 2018 - Klever X ALPHA 45 2021 - Stromer ST3 PINON 2023
- FreddyH
- Veteraan
- Berichten: 9406
- Lid geworden op: 24 jan 2018, 17:46
- Locatie: regio Eindhoven
- Merk SP: Klever X-Speed α
Kan je met een netwerk-sniffer een session id of zo achterhalen?joost schreef: ↑08 jul 2018, 22:26 Vandaag eens in de api van Stromer gedoken. Via deze url loopt de verbinding https://api3.stromer-portal.ch/rapi/mobile/v2/bike/ krijg alleen de authenticatie nog niet goed buiten de app.
There was never a good war, or a bad peace
[Benjamin Franklin]
-
- Expert
- Berichten: 102
- Lid geworden op: 23 dec 2017, 21:53
- Locatie: Dordrecht
- Merk SP: Stromer ST3
@FreddyH ik heb mitmproxy gebruikt. Mijn iPhone verbind direct met deze proxy en dan zie ik al het verkeer dat over de lijn gaat.
Op deze pagina kom je in de app https://api3.stromer-portal.ch/users/login/
Naast je credentials heb je ook nog een client_id nodig (die heb ik) alleen na de submit wordt je naar de app geredirect.
https://api3.stromer-portal.ch/users/lo ... nt_id=JEID
redirect_uri al wel aangepast naar api3.stromer-portal.ch maar dat gaat niet goed. Wellicht dat iemand de android apk eens kan uitpakken en zien wat daar gebeurd.
Op deze pagina kom je in de app https://api3.stromer-portal.ch/users/login/
Naast je credentials heb je ook nog een client_id nodig (die heb ik) alleen na de submit wordt je naar de app geredirect.
https://api3.stromer-portal.ch/users/lo ... nt_id=JEID
redirect_uri al wel aangepast naar api3.stromer-portal.ch maar dat gaat niet goed. Wellicht dat iemand de android apk eens kan uitpakken en zien wat daar gebeurd.
Stromer ST3 2020 Deep Black
Bodyfloat
Bodyfloat
- FreddyH
- Veteraan
- Berichten: 9406
- Lid geworden op: 24 jan 2018, 17:46
- Locatie: regio Eindhoven
- Merk SP: Klever X-Speed α
Je bent alweer een stapje verder. Ik zou graag ook een keer gaan hacken, maar heb zelf geen Stromer dus dat gaat hem niet worden... Wellicht weer een keer een paar dagen een 'proefrit' maken
Die Android APK uitpakken kun je zelf natuurlijk ook. Moet ik deze downloaden en naar je toe sturen?
Die Android APK uitpakken kun je zelf natuurlijk ook. Moet ik deze downloaden en naar je toe sturen?
There was never a good war, or a bad peace
[Benjamin Franklin]
Ik heb het inmiddels even uitgezocht met een beetje python code:
Ik weet niet of ik die client_id en client_secret zo mag posten, maar zijn zijn heel gemakkelijk uit de APK te halen (of door een MITM op de app). Ik ga dit gebruiken om een tooltje te maken dat verifieert dat de fiets goed wordt opgeladen. Ik heb heel soms dat ie na een uurtje ofzo stopt met opladen en dan kom ik bijna niet meer thuis. Straks checkt ie dat gewoon ieder kwartier: staat de fiets bij mijn werk en is de batterij niet aan het opladen ==> stuur een mailtje
Code: Selecteer alles
import requests
from IPython.display import HTML
from urllib.parse import urlencode, parse_qs, splitquery
password = 'xxx'
username = 'xxx'
client_id = 'xxxxxxx'
client_secret = 'xxxxxxxxxxxxx'
def get_code(client_id, username, password):
url = "https://api3.stromer-portal.ch/users/login/"
s = requests.session()
res = s.get(url)
HTML(res.text)
s.cookies
qs = urlencode(
{
"client_id": client_id,
"response_type": "code",
"redirect_url": "stromerauth://auth",
"scope": "bikeposition bikestatus bikeconfiguration bikelock biketheft bikedata bikepin bikeblink userprofile",
}
)
data = {
"password": password,
"username": username,
"csrfmiddlewaretoken": s.cookies.get("csrftoken"),
"next": "/o/authorize/?" + qs,
}
res = s.post(url, data=data, headers=dict(Referer=url), allow_redirects=False)
res = s.send(res.next, allow_redirects=False)
_, qs = splitquery(res.headers["Location"])
code = parse_qs(qs)["code"][0]
return code
def get_access_token(client_id, client_secret, code):
url = "https://api3.stromer-portal.ch//o/token/"
params = {
"grant_type": "authorization_code",
"client_id": client_id,
"client_secret": client_secret,
"code": code,
"redirect_uri": "stromerauth://auth",
}
res = requests.post(url, params=params)
return res.json()["access_token"]
def call_api(access_token, endpoint, params={}):
url = f"https://api3.stromer-portal.ch/rapi/mobile/v2/{endpoint}"
headers = {"Authorization": f"Bearer {access_token}"}
res = requests.get(url, headers=headers, params={})
return res.json()["data"][0]
Code: Selecteer alles
import json
endpoint="bike/"
bike = call_api(access_token, endpoint)
print('bike:', json.dumps(bike, indent=True))
bike = call_api(access_token, endpoint="bike/")
endpoint = f'bike/{bike["bikeid"]}/state/'
params = {'cached':'false'}
state = call_api(access_token, endpoint, params)
print('state:', json.dumps(state, indent=True))
bike: {
"bikeid": ***,
"biketype": "ST2",
"color": "silver",
"hardware": "omniinterface",
"bikemodel": "ST2",
"nickname": "***",
"size": "sport 20"
}
state: {
"battery_SOC": 40,
"suiversion": "3.4.2.1",
"bike_speed": 0.0,
"tntversion": "3.5",
"trip_time": 81401,
"light_on": 255,
"average_speed_trip": 34.9,
"trip_distance": 788.5,
"average_speed_total": 34.1,
"motor_temp": 16.0,
"average_energy_consumption": 141,
"power_on_cycles": 670,
"total_time": 318409,
"atmospheric_pressure": 0,
"battery_temp": 24.0,
"battery_health": 95,
"total_distance": 3013.6,
"assistance_level": 0,
"rcvts": 1538643543,
"theft_flag": false,
"lock_flag": true,
"total_energy_consumption": 42594
}
ST2 2016 - 983wh
Knap gedaan
Met die informatie ontstaan er best wat leuke mogelijkheden.
Met die informatie ontstaan er best wat leuke mogelijkheden.
ST3 Deep Black XL 983 Wh | Stuur 20mm rise & 20° backsweep | Thudbuster ST
Woon-werk & Stroomverbruikoverzicht
Woon-werk & Stroomverbruikoverzicht
Met dank aan @merstro heb ik een portje naar NodeJS gemaakt:
Code: Selecteer alles
const request = require("request-promise");
const qs = require("qs");
const cheerio = require("cheerio");
class Stromer {
constructor(options) {
this.client_id = options.client_id;
this.client_secret = options.client_secret;
this.username = options.username;
this.password = options.password;
this.jar = request.jar();
this.LOGIN_URL = "https://api3.stromer-portal.ch/users/login/";
this.TOKEN_URL = "https://api3.stromer-portal.ch/o/token/";
this.API_URL = "https://api3.stromer-portal.ch/rapi/mobile/v2/";
}
async getCode() {
const loginPage = await request({
url: this.LOGIN_URL,
jar: this.jar
});
const cookies = this.jar.getCookieString(this.LOGIN_URL);
const [_, csrf] = cookies.split("=");
const queryString = qs.stringify({
client_id: this.client_id,
response_type: "code",
redirect_url: "stromerauth://auth",
scope:
"bikeposition bikestatus bikeconfiguration bikelock biketheft bikedata bikepin bikeblink userprofile"
});
const $ = cheerio.load(loginPage);
const formCsrf = $("input[name=csrfmiddlewaretoken]").val();
const form = {
username: this.username,
password: this.password,
csrfmiddlewaretoken: formCsrf,
next: `/o/authorize/?${queryString}`
};
let code = "";
try {
const redirect = await request({
url: this.LOGIN_URL,
method: "post",
form,
jar: this.jar,
followRedirect: false,
headers: {
Referer: this.LOGIN_URL
}
});
} catch (e) {
try {
const authorize = await request({
url: "https://api3.stromer-portal.ch" + e.response.headers.location,
followRedirect: false,
jar: this.jar
});
} catch (e) {
const location = e.response.headers.location;
const [_, code] = location.split("=");
return code;
}
}
}
async getAccessToken(code) {
const data = {
grant_type: "authorization_code",
client_id: this.client_id,
client_secret: this.client_secret,
code,
redirect_uri: "stromerauth://auth"
};
const json = await request({
url: this.TOKEN_URL,
method: "post",
formData: data,
jar: this.jar
});
return JSON.parse(json).access_token;
}
async call(endpoint) {
if (this.accessToken) {
return await this._callApi(this.accessToken, endpoint);
} else {
const code = await this.getCode();
this.accessToken = await this.getAccessToken(code);
if (this.accessToken) {
return await this._callApi(this.accessToken, endpoint);
} else {
throw new Error("No access token could be retrieved");
}
}
}
async _callApi(access_token, endpoint) {
const response = await request({
url: `${this.API_URL}${endpoint}`,
headers: { Authorization: `Bearer ${access_token}` }
});
return JSON.parse(response).data;
}
}
Code: Selecteer alles
const test = new Stromer({
username: "xxxxx",
password: "xxxxxx",
client_id: "xxxxxxx",
client_secret: "xxxxxxx"
});
test.call("bike").then(c => console.log(c));