Skip to content

Commit

Permalink
Merge pull request #49 from syssi/update-api-version
Browse files Browse the repository at this point in the history
Migrate to API v5 endpoints and oauth2 token authentication
  • Loading branch information
marcelwestrahome authored May 31, 2023
2 parents bfdbc31 + fb4421c commit a2078fb
Show file tree
Hide file tree
Showing 7 changed files with 27 additions and 28 deletions.
29 changes: 19 additions & 10 deletions custom_components/niu/api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from .const import *
import hashlib
import requests
import json
from datetime import datetime, timedelta
# from homeassistant.util import Throttle
from time import gmtime, strftime

class NiuApi:
def __init__(self, username, password, country, scooter_id) -> None:
def __init__(self, username, password, scooter_id) -> None:
self.username = username
self.password = password
self.country = country
self.scooter_id = int(scooter_id)

self.dataBat = None
Expand All @@ -20,8 +20,8 @@ def __init__(self, username, password, country, scooter_id) -> None:
def initApi(self):
self.token = self.get_token()
api_uri = MOTOINFO_LIST_API_URI
self.sn = self.get_vehicles_info(api_uri)["data"][self.scooter_id]["sn"]
self.sensor_prefix = self.get_vehicles_info(api_uri)["data"][self.scooter_id]["name"]
self.sn = self.get_vehicles_info(api_uri)["data"]["items"][self.scooter_id]["sn_id"]
self.sensor_prefix = self.get_vehicles_info(api_uri)["data"]["items"][self.scooter_id]["scooter_name"]
self.updateBat()
self.updateMoto()
self.updateMotoInfo()
Expand All @@ -31,27 +31,33 @@ def initApi(self):
def get_token(self):
username = self.username
password = self.password
cc = self.country

url = ACCOUNT_BASE_URL + LOGIN_URI
data = {"account": username, "countryCode": cc, "password": password}
md5 = hashlib.md5(password.encode("utf-8")).hexdigest()
data = {
"account": username,
"password": md5,
"grant_type": "password",
"scope": "base",
"app_id": "niu_ktdrr960",
}
try:
r = requests.post(url, data=data)
except BaseException as e:
print(e)
return False
data = json.loads(r.content.decode())
return data["data"]["token"]
return data["data"]["token"]["access_token"]


def get_vehicles_info(self, path):

token = self.token

url = API_BASE_URL + path
headers = {"token": token, "Accept-Language": "en-US"}
headers = {"token": token}
try:
r = requests.post(url, headers=headers, data=[])
r = requests.get(url, headers=headers, data=[])
except ConnectionError:
return False
if r.status_code != 200:
Expand All @@ -66,7 +72,10 @@ def get_info(self,path, ):
url = API_BASE_URL + path

params = {"sn": sn}
headers = {"token": token, "Accept-Language": "en-US"}
headers = {
"token": token,
"user-agent": "manager/4.10.4 (android; IN2020 11);lang=zh-CN;clientIdentifier=Domestic;timezone=Asia/Shanghai;model=IN2020;deviceName=IN2020;ostype=android",
}
try:

r = requests.get(url, headers=headers, params=params)
Expand Down
3 changes: 1 addition & 2 deletions custom_components/niu/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:

username = niu_auth[CONF_USERNAME]
password = niu_auth[CONF_PASSWORD]
country = niu_auth[CONF_COUNTRY]
scooter_id = niu_auth[CONF_SCOOTER_ID]

api = NiuApi(username, password, country, scooter_id)
api = NiuApi(username, password, scooter_id)
await hass.async_add_executor_job(api.initApi)

camera_name = api.sensor_prefix + ' Last Track Camera'
Expand Down
9 changes: 3 additions & 6 deletions custom_components/niu/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
{
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_COUNTRY, default=ITALY_COUNTRY_ID): str,
vol.Required(CONF_SCOOTER_ID, default=DEFAULT_SCOOTER_ID): int,
vol.Required(CONF_SENSORS, default=AVAILABLE_SENSORS): selector.SelectSelector(
selector.SelectSelectorConfig(options=AVAILABLE_SENSORS,
Expand All @@ -37,15 +36,14 @@


class NiuAuthenticator:
def __init__(self, username, password, country, scooter_id, sensors_selected) -> None:
def __init__(self, username, password, scooter_id, sensors_selected) -> None:
self.username = username
self.password = password
self.country = country
self.scooter_id = scooter_id
self.sensors_selected = sensors_selected

async def authenticate(self, hass):
api = NiuApi(self.username, self.password, self.country, self.scooter_id)
api = NiuApi(self.username, self.password, self.scooter_id)
try:
token = await hass.async_add_executor_job(api.get_token)
if isinstance(token, bool):
Expand All @@ -71,10 +69,9 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None) -> Flo
if user_input != None:
username = user_input[CONF_USERNAME]
password = user_input[CONF_PASSWORD]
country = user_input[CONF_COUNTRY]
scooter_id = user_input[CONF_SCOOTER_ID]
sensors_selected = user_input[CONF_SENSORS]
niu_auth = NiuAuthenticator(username, password, country, scooter_id, sensors_selected)
niu_auth = NiuAuthenticator(username, password, scooter_id, sensors_selected)
auth_result = await niu_auth.authenticate(self.hass)
if auth_result:
return self.async_create_entry(title=integration_title, data={CONF_AUTH:niu_auth.__dict__})
Expand Down
9 changes: 3 additions & 6 deletions custom_components/niu/const.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
ACCOUNT_BASE_URL = "https://account-fk.niu.com"
LOGIN_URI = "/appv2/login"
LOGIN_URI = "/v3/api/oauth2/token"
API_BASE_URL = "https://app-api-fk.niu.com"
MOTOR_BATTERY_API_URI = "/v3/motor_data/battery_info"
MOTOR_INDEX_API_URI = "/v3/motor_data/index_info"
MOTOINFO_LIST_API_URI = "/motoinfo/list"
MOTOR_INDEX_API_URI = "/v5/scooter/motor_data/index_info"
MOTOINFO_LIST_API_URI = "/v5/scooter/list"
MOTOINFO_ALL_API_URI = "/motoinfo/overallTally"
TRACK_LIST_API_URI = "/v5/track/list/v2"
# FIRMWARE_BAS_URL = '/motorota/getfirmwareversion'

DOMAIN = "niu"
CONF_USERNAME = "username"
CONF_PASSWORD = "password"
CONF_COUNTRY = "country"
CONF_SCOOTER_ID = "scooter_id"
CONF_AUTH = 'conf_auth'
CONF_SENSORS = "sensors_selected"

DEFAULT_SCOOTER_ID = 0
ITALY_COUNTRY_ID = "39"

SENSOR_TYPE_BAT = "BAT"
SENSOR_TYPE_MOTO = "MOTO"
Expand Down Expand Up @@ -67,7 +65,6 @@
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_COUNTRY): cv.positive_int,
vol.Optional(CONF_SCOOTER_ID, default=DEFAULT_SCOOTER_ID): cv.positive_int,
vol.Optional(CONF_MONITORED_VARIABLES, default=["BatteryCharge"]): vol.All(
cv.ensure_list,
Expand Down
3 changes: 1 addition & 2 deletions custom_components/niu/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:

username = niu_auth[CONF_USERNAME]
password = niu_auth[CONF_PASSWORD]
country = niu_auth[CONF_COUNTRY]
scooter_id = niu_auth[CONF_SCOOTER_ID]
sensors_selected = niu_auth[CONF_SENSORS]

api = NiuApi(username, password, country, scooter_id)
api = NiuApi(username, password, scooter_id)
await hass.async_add_executor_job(api.initApi)

# add sensors
Expand Down
1 change: 0 additions & 1 deletion custom_components/niu/string.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"data": {
"CONF_USERNAME": "[%key:common::config_flow::data::conf_username%]",
"CONF_PASSWORD": "[%key:common::config_flow::data::conf_password%]",
"CONF_COUNTRY": "[%key:common::config_flow::data::conf_country%]",
"CONF_SCOOTER_ID": "[%key:common::config_flow::data::conf_scooter_id%]"
}
}
Expand Down
1 change: 0 additions & 1 deletion custom_components/niu/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"data": {
"username": "Username",
"password": "Password",
"country": "Country id",
"scooter_id": "Scooter id",
"sensors_selected": "Select which sensor to integrate"
}
Expand Down

0 comments on commit a2078fb

Please sign in to comment.