Toegang krijgen tot de OMNI API

Voor (elektro)technische vragen, fietsonderhoud en -onderdelen.
joost
Pro
Pro
Berichten: 48
Lid geworden op: 23 dec 2017, 21:53
Locatie: Dordrecht

Toegang krijgen tot de OMNI API

Bericht door joost » 05 mar 2018, 10:31

[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.
🇳🇱 Stromer ST1 X 2019 Ocean blue 🚀

Gebruikersavatar
Stromeur
Admin | Forum Supporter
Admin | Forum Supporter
Berichten: 6560
Lid geworden op: 15 mei 2017, 20:18
Locatie: Harlingen

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Bericht door Stromeur » 05 mar 2018, 10:46

joost schreef:
05 mar 2018, 10:31
een leuk projectje.
Zeker! Open daar tegen die tijd gerust een topic over als je zin hebt om je ervaringen hiermee te delen.
Stromer ST2 S 2017. Mods & Excessen: OMNI Interface C, Stromer/Suntour geveerde voorvork, BodyFloat, Ergotec M88, GoPro Hero4, Rokform Pro, Ergon GP3, SKS spatlap, Spurcycle, Ortlieb Back-Roller High Visibility.

joost
Pro
Pro
Berichten: 48
Lid geworden op: 23 dec 2017, 21:53
Locatie: Dordrecht

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Bericht door joost » 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.

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 :ay
🇳🇱 Stromer ST1 X 2019 Ocean blue 🚀

Gebruikersavatar
Stromeur
Admin | Forum Supporter
Admin | Forum Supporter
Berichten: 6560
Lid geworden op: 15 mei 2017, 20:18
Locatie: Harlingen

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Bericht door Stromeur » 08 jul 2018, 22:29

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!
Stromer ST2 S 2017. Mods & Excessen: OMNI Interface C, Stromer/Suntour geveerde voorvork, BodyFloat, Ergotec M88, GoPro Hero4, Rokform Pro, Ergon GP3, SKS spatlap, Spurcycle, Ortlieb Back-Roller High Visibility.

Gebruikersavatar
FreddyH
Veteraan | Forum Supporter
Veteraan | Forum Supporter
Berichten: 1490
Lid geworden op: 24 jan 2018, 17:46
Locatie: regio Eindhoven
Contacteer:

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Bericht door FreddyH » 08 jul 2018, 22:30

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.
Kan je met een netwerk-sniffer een session id of zo achterhalen?
Bulls Green Mover E45 (GoSwiss, 1260 Wh accu) sinds 10-2014
Extra's: Back-Roller Classic - Smarthalo - NCX-SP12 - Spurcycle - Marathon GT365 - "Bouwlamp"

joost
Pro
Pro
Berichten: 48
Lid geworden op: 23 dec 2017, 21:53
Locatie: Dordrecht

Re: Nieuwe Stromer OMNI technologie 2018: OMNI Connect

Bericht door joost » 09 jul 2018, 19:49

@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.
🇳🇱 Stromer ST1 X 2019 Ocean blue 🚀

Gebruikersavatar
FreddyH
Veteraan | Forum Supporter
Veteraan | Forum Supporter
Berichten: 1490
Lid geworden op: 24 jan 2018, 17:46
Locatie: regio Eindhoven
Contacteer:

Re: Toegang krijgen tot de OMNI API

Bericht door FreddyH » 13 jul 2018, 19:44

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 :dwarf:

Die Android APK uitpakken kun je zelf natuurlijk ook. Moet ik deze downloaden en naar je toe sturen?
Bulls Green Mover E45 (GoSwiss, 1260 Wh accu) sinds 10-2014
Extra's: Back-Roller Classic - Smarthalo - NCX-SP12 - Spurcycle - Marathon GT365 - "Bouwlamp"

merstro
Rookie
Rookie
Berichten: 7
Lid geworden op: 22 feb 2018, 19:54

Re: Toegang krijgen tot de OMNI API

Bericht door merstro » 04 okt 2018, 11:05

Ik heb het inmiddels even uitgezocht met een beetje python code:

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
}

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
ST2 2016 - 983wh

Gebruikersavatar
JeroenDG
Pro
Pro
Berichten: 43
Lid geworden op: 22 aug 2018, 09:42
Locatie: Aalst, BE
Contacteer:

Re: Toegang krijgen tot de OMNI API

Bericht door JeroenDG » 04 okt 2018, 11:52

Knap gedaan :ay
Met die informatie ontstaan er best wat leuke mogelijkheden.
Wachtend op ST3 Deep Black XL
Woon-werk & Stroomverbuikoverzicht

TimB
Runner-up
Runner-up
Berichten: 12
Lid geworden op: 19 apr 2018, 08:31

Re: Toegang krijgen tot de OMNI API

Bericht door TimB » 05 okt 2018, 15:33

merstro schreef:
04 okt 2018, 11:05
Ik heb het inmiddels even uitgezocht met een beetje python code:
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;
  }
}
Gebruik:

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));

Plaats reactie

Wie is er online

Gebruikers op dit forum: Geen geregistreerde gebruikers en 1 gast