diff --git a/.gitignore b/.gitignore index 606a10b..0aa3360 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.buildozer # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 85b8957..12ee9e4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # huafetcher +Kivy GUI for huami-token. Works on desktop and as Android apk. Downloads key and aGPS data, unzips it into `/storage/emulated/0` + +[huami-token](https://github.com/argrento/huami-token): all credits to the original author + +## Install + +Install [Buildozer](https://github.com/kivy/buildozer/) and [Kivy](https://github.com/kivy/kivy) + +pip install buildozer +pip install kivy + +or + +pip install -r requirements.txt + +## Run + +python main.py + +## make Android apk + +buildozer -v android debug deploy run + diff --git a/data/icon.png b/data/icon.png new file mode 100644 index 0000000..0c74ddc Binary files /dev/null and b/data/icon.png differ diff --git a/data/icon.svg b/data/icon.svg new file mode 100644 index 0000000..b3c0442 --- /dev/null +++ b/data/icon.svg @@ -0,0 +1,80 @@ + + + + + + image/svg+xml + + + + + + + + + + hf + diff --git a/huami_token.py b/huami_token.py new file mode 100644 index 0000000..7ccaf8d --- /dev/null +++ b/huami_token.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 + +import json +import uuid +import random +import shutil +import urllib +import requests + +import urls + + +class HuamiAmazfit: + def __init__(self, method="amazfit", email=None, password=None): + + #if method == 'amazfit' and (not email or not password): + # raise ValueError("For Amazfit method E-Mail and Password can not be null.") + self.method = method + self.email = email + self.password = password + self.access_token = None + self.country_code = None + + self.app_token = None + self.login_token = None + self.user_id = None + + self.result = None + + self.r = str(uuid.uuid4()) + + # IMEI or something unique + self.device_id = "02:00:00:%02x:%02x:%02x" % (random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255)) + def __str__(self): + return f"{self.email} {self.password} {self.method} {self.access_token}" + + def parse_token(self, token_url): + + parsed_token_url = urllib.parse.urlparse(token_url) + token_url_parameters = urllib.parse.parse_qs(parsed_token_url.query) + + if 'code' not in token_url_parameters: + return + + self.access_token = token_url_parameters['code'] + self.country_code = 'US' + + def get_access_token(self): + print(f"Getting access token with {self.method} login method...") + + if self.method == 'xiaomi': + login_url = urls.URLS["login_xiaomi"] + + + + elif self.method == 'amazfit': + + auth_url = urls.URLS['tokens_amazfit'].format(user_email=urllib.parse.quote(self.email)) + + data = urls.PAYLOADS['tokens_amazfit'] + data['password'] = self.password + + response = requests.post(auth_url, data=data, allow_redirects=False) + response.raise_for_status() + + # 'Location' parameter contains url with login status + redirect_url = urllib.parse.urlparse(response.headers.get('Location')) + redirect_url_parameters = urllib.parse.parse_qs(redirect_url.query) + + if 'error' in redirect_url_parameters: + raise ValueError(f"Wrong E-mail or Password. Error: {redirect_url_parameters['error']}") + + if 'access' not in redirect_url_parameters: + raise ValueError("No 'access' parameter in login url.") + + if 'country_code' not in redirect_url_parameters: + raise ValueError("No 'country_code' parameter in login url.") + + self.access_token = redirect_url_parameters['access'] + self.country_code = redirect_url_parameters['country_code'] + + + def login(self, external_token=None): + print("Logging in...") + if external_token: + self.access_token = external_token + + login_url = urls.URLS['login_amazfit'] + + data = urls.PAYLOADS['login_amazfit'] + data['country_code'] = self.country_code + data['device_id'] = self.device_id + data['third_name'] = 'huami' if self.method == 'amazfit' else 'mi-watch' + data['code'] = self.access_token + data['grant_type'] = 'access_token' if self.method == 'amazfit' else 'request_token' + + response = requests.post(login_url, data=data, allow_redirects=False) + response.raise_for_status() + login_result = response.json() + + if 'error_code' in login_result: + raise ValueError(f"Login error. Error: {login_result['error_code']}") + + if 'token_info' not in login_result: + raise ValueError("No 'token_info' parameter in login data.") + else: + token_info = login_result['token_info'] + if 'app_token' not in token_info: + raise ValueError("No 'app_token' parameter in login data.") + self.app_token = token_info['app_token'] + + if 'login_token' not in token_info: + raise ValueError("No 'login_token' parameter in login data.") + self.login_token = token_info['login_token'] + + if 'user_id' not in token_info: + raise ValueError("No 'user_id' parameter in login data.") + self.user_id = token_info['user_id'] + print("Logged in! User id: {}".format(self.user_id)) + return True + + def get_wearable_auth_keys(self): + print("Getting linked wearables...") + print(self.user_id) + + devices_url = urls.URLS['devices'].format(user_id=urllib.parse.quote(self.user_id)) + + headers = urls.PAYLOADS['devices'] + headers['apptoken'] = self.app_token + + response = requests.get(devices_url, headers=headers) + response.raise_for_status() + device_request = response.json() + if 'items' not in device_request: + raise ValueError("No 'items' parameter in devices data.") + devices = device_request['items'] + + devices_dict = {} + + for idx, wearable in enumerate(devices): + if 'macAddress' not in wearable: + raise ValueError("No 'macAddress' parameter in device data.") + mac_address = wearable['macAddress'] + + if 'additionalInfo' not in wearable: + raise ValueError("No 'additionalInfo' parameter in device data.") + device_info = json.loads(wearable['additionalInfo']) + + if 'auth_key' not in device_info: + raise ValueError("No 'auth_key' parameter in device data.") + key_str = device_info['auth_key'] + auth_key = '0x' + (key_str if key_str != '' else '00') + + devices_dict[f'{mac_address}'] = auth_key + + return devices_dict + + def get_gps_data(self): + agps_packs = ["AGPS_ALM", "AGPSZIP"] + agps_file_names = ["cep_alm_pak.zip", "cep_7days.zip"] + agps_link = urls.URLS['agps'] + + headers = urls.PAYLOADS['agps'] + headers['apptoken'] = self.app_token + + for idx, agps_pack_name in enumerate(agps_packs): + print("Downloading {}...".format(agps_pack_name)) + response = requests.get(agps_link.format(pack_name=agps_pack_name), headers=headers) + response.raise_for_status() + agps_result = response.json()[0] + if 'fileUrl' not in agps_result: + raise ValueError("No 'fileUrl' parameter in files request.") + with requests.get(agps_result['fileUrl'], stream=True) as r: + with open(agps_file_names[idx], 'wb') as f: + shutil.copyfileobj(r.raw, f) + + def logout(self): + logout_url = urls.URLS['logout'] + + data = urls.PAYLOADS['logout'] + data['login_token'] = self.login_token + + response = requests.post(logout_url, data=data) + logout_result = response.json() + + if logout_result['result'] == 'ok': + print("\nLogged out.") + else: + print("\nError logging out.") + + diff --git a/main.py b/main.py new file mode 100644 index 0000000..fc011ef --- /dev/null +++ b/main.py @@ -0,0 +1,394 @@ +from kivy.app import App +from kivy.uix.button import Button +from kivy.uix.label import Label +from kivy.lang import Builder +from kivy.utils import platform +from kivy.uix.textinput import TextInput +from kivy.uix.boxlayout import BoxLayout +from functools import partial +from kivy.clock import Clock +from kivy.logger import Logger +from kivy.uix.dropdown import DropDown +from huami_token import HuamiAmazfit +import urls as urls +from kivy.core.clipboard import Clipboard +from kivy.storage.jsonstore import JsonStore + +DEBUG=False +DEBUG=True + +def debug_print(*xargs): + if DEBUG: + print(*xargs) + +SPACING=2 +Builder.load_string(''' +: + font_size: 30 + halign: 'left' + color: 1,1,1,1 + bcolor: .1, 0, .6, 0 + canvas.before: + Color: + rgba: root.bcolor + Rectangle: + size: (self.width -2, self.height -2) + pos: (self.x+1,self.y +1) + + +: + font_size: 30 + halign: 'center' + bcolor: .7, .7, .7, 1 + canvas.before: + Color: + rgba: root.bcolor + Rectangle: + size: self.size + pos: self.pos + +: + font_size: 30 + halign: 'left' + bcolor: .7, .7, .7, 1 + canvas.before: + Color: + rgba: root.bcolor + Rectangle: + size: self.size + pos: self.pos +: + font_size: 30 + bcolor: .7, .7, .7, 1 + background_color: .1, 0, .5, 0 + canvas.before: + Color: + rgba: root.bcolor + Rectangle: + size: self.size + pos: self.pos + + ''') +class MyLabel(Label): + pass + +class MyLeftLabel(Label): + pass + +class MyButton(Button): + pass + +class MyInput(TextInput): + pass + + +class Main(App): + def build(self): + self.huamidevice=HuamiAmazfit() + self.store = JsonStore('credentials.json') + + screen_layout = BoxLayout(orientation="vertical", spacing=SPACING) + buttons_layout = BoxLayout(orientation="horizontal", spacing=SPACING) + rows_layout = BoxLayout(orientation="vertical", spacing=SPACING) + + self.instructions_label=MyLeftLabel(text='Huafetcher') + rows_layout.add_widget(self.instructions_label) + + dropdown = DropDown() + + xiaomi_button = MyButton(text='Xiaomi', size_hint_y=None, height=150) + xiaomi_button.bind(on_press=lambda a:self.set_login_method('xiaomi')) + xiaomi_button.bind(on_release=lambda btn: dropdown.select(btn.text)) + amazfit_button = MyButton(text='Amazfit', size_hint_y=None, height=150) + amazfit_button.bind(on_press=lambda a:self.set_login_method('amazfit')) + amazfit_button.bind(on_release=lambda btn: dropdown.select(btn.text)) + + dropdown.add_widget(xiaomi_button) + dropdown.add_widget(amazfit_button) + + dropdown_button = MyButton(text='Login method') + dropdown_button.bind(on_release=dropdown.open) + #dropdown_button.bind(on_press=lambda x: setattr(dropdown_button,'text','Select')) + dropdown.bind(on_select=lambda instance, x: setattr(dropdown_button, 'text', x)) + + get_token_button = MyButton(text='Get token') + get_token_button.bind(on_press=self.on_press_button_gettoken) + + fetch_key_button = MyButton(text='Fetch key') + fetch_key_button.bind(on_press=self.on_press_button_fetch_key) + + fetch_agps_button = MyButton(text='Fetch aGPS') + fetch_agps_button.bind(on_press=self.on_press_button_agps) + + share_agps_button = MyButton(text='Share aGPS') + share_agps_button.bind(on_press=self.on_press_button_share_agps) + + buttons_layout.add_widget(get_token_button) + #buttons_layout.add_widget(login_button) + buttons_layout.add_widget(fetch_key_button) + buttons_layout.add_widget(fetch_agps_button) + #sharing doesn't work + #buttons_layout.add_widget(share_agps_button) + + paste_token_input_layout = BoxLayout(orientation="horizontal", spacing=SPACING) + paste_token_input_label=MyLabel(text='URL result') + + self.paste_token_input = MyInput(text='', + multiline=False, + ) + self.paste_token_input.bind(text=self.set_token) + + + paste_button1=MyButton(text='Paste', size_hint=(.3, 1)) + paste_button1.bind(on_press=lambda instance: self.on_press_paste(self.paste_token_input) ) + + paste_token_input_layout.add_widget(paste_token_input_label) + paste_token_input_layout.add_widget(paste_button1) + paste_token_input_layout.add_widget(self.paste_token_input) + credentials_email_label=MyButton(text='Email' , size_hint=(.7, 1)) + + self.credentials_email_input = MyInput(text='', + multiline=False, + ) + self.credentials_email_input.bind(text=lambda instance,x: setattr(self.huamidevice,'email',x)) + credentials_email_label.bind(on_press=self.on_press_paste_email) + + paste_button2=MyButton(text='Paste', size_hint=(.3, 1)) + paste_button2.bind(on_press=lambda instance: self.on_press_paste(self.credentials_email_input) ) + + save_button1=MyButton(text='Save', size_hint=(.3, 1)) + #load_button1.bind(on_press=lambda instance: ) + save_button1.bind(on_press=lambda instance: self.on_press_save(self.credentials_email_input, 'email') ) + + + + self.credentials_email_layout = BoxLayout(orientation="horizontal", spacing=SPACING) + self.credentials_email_layout.add_widget(credentials_email_label) + #credentials_email_layout.add_widget(load_button1) + self.credentials_email_layout.add_widget(save_button1) + self.credentials_email_layout.add_widget(paste_button2) + self.credentials_email_layout.add_widget(self.credentials_email_input) + + + credentials_password_label=MyLabel(text='Password', size_hint=(.7, 1)) + + self.credentials_password_input = MyInput(text='', + multiline=False, + ) + self.credentials_password_input.bind(text=lambda instance,x: setattr(self.huamidevice,'password',x)) + credentials_password_label.bind(on_press=self.on_press_paste_password) + + paste_button3=MyButton(text='Paste', size_hint=(.3, 1)) + paste_button3.bind(on_press=lambda instance: self.on_press_paste(self.credentials_password_input) ) + + save_button2=MyButton(text='Save', size_hint=(.3, 1)) + save_button2.bind(on_press=lambda instance: self.on_press_save(self.credentials_password_input, 'password') ) + + self.credentials_password_layout = BoxLayout(orientation="horizontal", spacing=SPACING) + self.credentials_password_layout.add_widget(credentials_password_label) + self.credentials_password_layout.add_widget(save_button2) + self.credentials_password_layout.add_widget(paste_button3) + self.credentials_password_layout.add_widget(self.credentials_password_input) + + rows_layout.add_widget(dropdown_button) + rows_layout.add_widget(self.credentials_email_layout) + rows_layout.add_widget(self.credentials_password_layout) + rows_layout.add_widget(paste_token_input_layout) + + result_value_label=MyButton(text='Found key') + self.result_value_value=TextInput() + + + copy_button4=MyButton(text='Copy', size_hint=(.3, 1)) + copy_button4.bind(on_press=lambda instance: self.on_press_copy(self.result_value_value) ) + + + result_value_layout = BoxLayout(orientation="horizontal", spacing=SPACING) + result_value_layout.add_widget(result_value_label) + result_value_layout.add_widget(copy_button4) + result_value_layout.add_widget(self.result_value_value) + result_value_label.bind(on_press=self.on_press_copy_result) + + + rows_layout.add_widget(result_value_layout) + + + rows_layout.add_widget(buttons_layout) + + + screen_layout.add_widget(rows_layout) + + self.on_press_load(self.credentials_email_input, 'email') + self.on_press_load(self.credentials_password_input, 'password') + self.set_login_method('xiaomi') + dropdown_button.text='Xiaomi' + + return screen_layout + + def hide_widget(self, wid, dohide=True): + debug_print(dohide) + if hasattr(wid, 'saved_attrs'): + debug_print(wid.saved_attrs) + if not dohide: + wid.height, wid.size_hint_y, wid.opacity, wid.disabled = wid.saved_attrs + del wid.saved_attrs + elif dohide: + wid.saved_attrs = wid.height, wid.size_hint_y, wid.opacity, wid.disabled + wid.height, wid.size_hint_y, wid.opacity, wid.disabled = 0, None, 0, True + + + def set_login_method(self,method): + debug_print(method) + self.huamidevice.method=method + if method == 'xiaomi': + self.hide_widget(self.credentials_email_layout, dohide=True) + self.hide_widget(self.credentials_password_layout, dohide=True) + else: + self.hide_widget(self.credentials_email_layout, dohide=False) + self.hide_widget(self.credentials_password_layout, dohide=False) + + def set_token(self, instance, text): + debug_print("got", text) + debug_print(self.huamidevice) + self.huamidevice.parse_token(text) + debug_print(self.huamidevice) + + def on_press_button_gettoken(self, instance): + debug_print('You pressed the button login!') + debug_print(self.huamidevice) + self.instructions_label.text="log in and paste url here" + if self.huamidevice.method == 'xiaomi': + login_url = urls.URLS["login_xiaomi"] + if ( platform != 'android' ): + import webbrowser + webbrowser.open(login_url, new = 2) + else: + from jnius import autoclass + from jnius import cast + + PythonActivity = autoclass('org.kivy.android.PythonActivity') + Intent = autoclass('android.content.Intent') + Uri = autoclass('android.net.Uri') + intent = Intent() + intent.setAction(Intent.ACTION_VIEW) + intent.setData(Uri.parse(login_url)) + currentActivity = cast('android.app.Activity', PythonActivity.mActivity) + currentActivity.startActivity(intent) + + self.huamidevice.get_access_token() + + + def on_press_button_fetch_key(self, instance): + debug_print('You pressed the button fetch!') + debug_print(self.huamidevice) + self.instructions_label.text="signing in" + if (self.huamidevice.login()): + self.instructions_label.text="Signed in as: {}, getting data".format(self.huamidevice.user_id) + + + device_keys = self.huamidevice.get_wearable_auth_keys() + self.instructions_label.text="Done" + self.result_value_value.text="" + for device_key in device_keys: + debug_print(f"{device_key} {device_keys[device_key]}") + + self.result_value_value.text=f"{device_keys[device_key]}" + + #Clock.schedule_once(partial(self.doit), 1) + + def on_press_paste_token(self, instance): + self.paste_token_input.text=Clipboard.paste() + + def on_press_paste_email(self, instance): + self.credentials_email_input.text=Clipboard.paste() + + def on_press_paste_password(self, instance): + self.credentials_password_input.text=Clipboard.paste() + + def on_press_copy_result(self, instance): + Clipboard.copy(self.result_value_value.text) + + def on_press_paste(self, instance): + instance.text=Clipboard.paste() + + def on_press_copy(self, instance): + Clipboard.copy(instance.text) + + def on_press_load(self, instance, key): + if self.store.exists(key): + instance.text=self.store.get(key)["value"] + + def on_press_save(self, instance, key): + self.store.put(key, value=instance.text) + + def on_press_button_agps(self, instance): + import zipfile + debug_print('You pressed the button agps!') + debug_print(self.huamidevice) + self.instructions_label.text="signing in" + if (self.huamidevice.login()): + self.instructions_label.text="Signed in as: {}, getting data".format(self.huamidevice.user_id) + + self.huamidevice.get_gps_data() + agps_file_names = ["cep_alm_pak.zip"] + if ( platform == 'android' ): + import shutil + import os + from jnius import autoclass + from jnius import cast + Environment = autoclass('android.os.Environment') + File = autoclass('java.io.File') + data_dir = Environment.getExternalStorageDirectory().getPath() + debug_print(data_dir) + for filename in agps_file_names: + sdpathfile = os.path.join(data_dir, filename) + shutil.copyfile(filename, sdpathfile) + with zipfile.ZipFile(filename, "r") as zip_f: + #zip_f.extractall() + zip_f.extractall(data_dir) + + self.instructions_label.text="Done" + #Clock.schedule_once(partial(self.doit), 1) + + def on_press_button_share_agps(self, instance): + #not working because android broke it + if ( platform == 'android' ): + import os + from jnius import autoclass + from jnius import cast + Environment = autoclass('android.os.Environment') + File = autoclass('java.io.File') + data_dir = Environment.getExternalStorageDirectory().getPath() + + PythonActivity = autoclass('org.kivy.android.PythonActivity') + Intent = autoclass('android.content.Intent') + Uri = autoclass('android.net.Uri') + intent = Intent() + intent.setAction(Intent.ACTION_VIEW) + data_dir = getattr(self, 'user_data_dir') + file_target=File(os.path.join(data_dir, "cep_pak.bin")) + #target=Uri.parse(os.path.join("file:///", data_dir, "cep_pak.bin")) + target=Uri.fromFile(file_target) + #intent.setData(Uri.parse(os.path.join("file:///", data_dir, "cep_pak.bin"))) + #intent.setType("application/octet-stream") + intent.setDataAndType(target, "application/octet-stream") + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + currentActivity = cast('android.app.Activity', PythonActivity.mActivity) + currentActivity.startActivity(intent) + + #intent = Intent() + #intent.setAction(Intent.ACTION_VIEW) + #intent.setData(Uri.parse("gps_alm.bin")) + #currentActivity = cast('android.app.Activity', PythonActivity.mActivity) + #currentActivity.startActivity(intent) + + + #def doit(self, *kargs): + + def openweb(url): + debug_print("open ", url) + +if __name__ == '__main__': + app = Main() + app.run() + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fc35816 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,221 @@ +aiofile==1.4.3 +aiofiles==0.4.0 +aiohttp==3.5.4 +aiohttp-cors==0.7.0 +aiosqlite==0.8.0 +altgraph==0.16.1 +appdirs==1.4.4 +argon2-cffi==20.1.0 +arrow==0.17.0 +asciimatics==1.11.0 +asn1crypto==0.24.0 +astral==1.9.2 +async-timeout==3.0.1 +atomicwrites==1.3.0 +attrs==18.2.0 +backcall==0.2.0 +bcrypt==3.1.5 +beautifulsoup4==4.6.3 +binaryornot==0.4.4 +bleach==3.1.5 +boltons==18.0.1 +boto3==1.9.106 +botocore==1.12.106 +briefcase==0.3.3 +brython==3.6.2 +bs4==0.0.1 +buildozer==1.2.0 +cachetools==4.1.0 +certifi==2018.11.29 +cffi==1.12.2 +chardet==3.0.4 +cli-helpers==2.1.0 +click==7.1.2 +colorama==0.4.3 +colosseum==0.2.0 +commonmark==0.9.1 +configobj==5.0.6 +cookiecutter==1.7.2 +cryptography==2.9 +cycler==0.10.0 +Cython==0.29.21 +dateparser==0.7.0 +decorator==4.4.2 +defusedxml==0.6.0 +distlib==0.3.1 +distro==1.4.0 +docutils==0.14 +ecdsa==0.16.0 +entrypoints==0.3 +enum34==1.1.10 +envs==1.3 +et-xmlfile==1.0.1 +face==0.1.0 +feedparser==6.0.2 +filelock==3.0.12 +Flask==0.12.2 +Flask-SQLAlchemy==2.3.2 +future==0.18.2 +gbulb==0.6.1 +gitdb==4.0.5 +GitPython==3.1.11 +glom==18.4.0 +gTTS-token==1.1.3 +h11==0.9.0 +h2==3.2.0 +home-assistant-frontend==20190220.0 +homeassistant==0.88.2 +hpack==3.0.0 +humanize==0.5.1 +hyperframe==5.2.0 +idna==2.7 +ifaddr==0.1.6 +importlib-metadata==1.6.0 +ipykernel==5.3.4 +ipython==7.18.1 +ipython-genutils==0.2.0 +ipywidgets==7.5.1 +iso8601==0.1.12 +itsdangerous==0.24 +jdcal==1.4.1 +jedi==0.17.2 +Jinja2==2.10 +jinja2-time==0.2.0 +jmespath==0.9.4 +joblib==0.16.0 +jsonschema==3.2.0 +jupyter==1.0.0 +jupyter-client==6.1.7 +jupyter-console==6.2.0 +jupyter-core==4.6.3 +Kivy==1.11.1 +Kivy-Garden==0.1.4 +kiwisolver==1.0.1 +litecli==1.4.1 +Logbook==1.5.3 +lxml==4.2.5 +macholib==1.11 +MarkupSafe==1.0 +matplotlib==3.0.0 +matrix-nio==0.10.0 +mistune==0.8.4 +MouseInfo==0.1.2 +multidict==4.4.2 +mutagen==1.42.0 +nbconvert==5.6.1 +nbformat==5.0.7 +netdisco==2.3.0 +notebook==6.1.3 +Nuitka==0.6.3.1 +numpy==1.15.2 +opencv-python==4.1.2.30 +openpyxl==3.0.3 +packaging==20.4 +paho-mqtt==1.4.0 +pandas==0.24.0 +pandocfilters==1.4.2 +parso==0.7.1 +patsy==0.5.1 +peewee==3.13.2 +pefile==2018.8.8 +pep517==0.6.0 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==6.2.1 +ply==3.11 +poyo==0.5.0 +prometheus-client==0.8.0 +prompt-toolkit==3.0.7 +ptyprocess==0.6.0 +PyAutoGUI==0.9.48 +pycairo==1.20.0 +pycparser==2.19 +pycryptodome==3.3.1 +pyfiglet==0.8.post1 +pygal==2.4.0 +PyGetWindow==0.0.8 +Pygments==2.6.1 +PyGObject==3.38.0 +pyjsparser==2.7.1 +PyJWT==1.6.4 +PyMsgBox==1.0.6 +pyodbc==4.0.24 +pyOpenSSL==19.1.0 +pyotp==2.2.6 +pyparsing==2.2.2 +pyperclip==1.7.0 +PyQRCode==1.2.1 +PyRect==0.1.4 +pyrsistent==0.16.0 +PyScreeze==0.1.25 +pyserial==3.4 +python-dateutil==2.7.3 +python-jose-cryptodome==1.3.2 +python-magic==0.4.15 +python-olm==3.1.3 +python-slugify==4.0.1 +python-xlib==0.23 +python3-xlib==0.15 +pytoml==0.1.21 +PyTweening==1.0.3 +pytz==2018.9 +PyYAML==3.13 +pyzmq==19.0.2 +qtconsole==4.7.6 +QtPy==1.9.0 +regex==2019.2.7 +requests==2.25.0 +rich==9.0.1 +ruamel.yaml==0.15.88 +s3transfer==0.2.0 +scikit-learn==0.23.2 +scipy==1.2.0 +selenium==3.141.0 +Send2Trash==1.5.0 +serial==0.0.97 +sgmllib3k==1.0.0 +sh==1.14.1 +simplegeneric==0.8.1 +six==1.15.0 +sklearn==0.0 +slimit==0.8.1 +smmap==3.0.4 +SQLAlchemy==1.2.2 +sqlparse==0.3.1 +statsmodels==0.12.0 +tabulate==0.8.7 +terminado==0.8.3 +terminaltables==3.1.0 +testpath==0.4.4 +text-unidecode==1.3 +threadpoolctl==2.1.0 +toga-core==0.3.0.dev25 +toga-gtk==0.3.0.dev25 +toml==0.10.2 +tornado==6.0.4 +traitlets==4.3.3 +travertino==0.1.3 +typing-extensions==3.7.4.3 +tzlocal==1.5.1 +ua-parser==0.8.0 +Unidecode==1.0.23 +unpaddedbase64==1.1.0 +urllib3==1.23 +urwid==2.1.0 +user-agents==1.1.0 +virtualenv==20.1.0 +voluptuous==0.11.5 +voluptuous-serialize==2.0.0 +warrant==0.6.1 +wcwidth==0.2.5 +webcolors==1.11.1 +webencodings==0.5.1 +Werkzeug==0.16.0 +widgetsnbextension==3.5.1 +xlib==0.21 +xlrd==1.2.0 +xmltodict==0.11.0 +yarl==1.2.6 +yattag==1.10.0 +zeroconf==0.21.3 +zipp==3.1.0 diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..8836b6d --- /dev/null +++ b/urls.py @@ -0,0 +1,53 @@ +URLS = { + 'login_xiaomi': 'https://account.xiaomi.com/oauth2/authorize?skip_confirm=false&' + 'client_id=2882303761517383915&pt=0&scope=1+6000+16001+20000&' + 'redirect_uri=https%3A%2F%2Fhm.xiaomi.com%2Fwatch.do&_locale=en_US&response_type=code', + 'tokens_amazfit': 'https://api-user.huami.com/registrations/{user_email}/tokens', + 'login_amazfit': 'https://account.huami.com/v2/client/login', + 'devices': 'https://api-mifit-us2.huami.com/users/{user_id}/devices', + 'agps': 'https://api-mifit-us2.huami.com/apps/com.huami.midong/fileTypes/{pack_name}/files', + 'data_short': 'https://api-mifit-us2.huami.com/users/{user_id}/deviceTypes/4/data', + 'logout': 'https://account-us2.huami.com/v1/client/logout' +} + +PAYLOADS = { + 'login_xiaomi': None, + 'tokens_amazfit': { + 'state': 'REDIRECTION', + 'client_id': 'HuaMi', + 'password': None, + 'redirect_uri': 'https://s3-us-west-2.amazonws.com/hm-registration/successsignin.html', + 'region': 'us-west-2', + 'token': 'access', + 'country_code': 'US' + }, + 'login_amazfit': { + 'dn': 'account.huami.com,api-user.huami.com,app-analytics.huami.com,api-watch.huami.com,' + 'api-analytics.huami.com,api-mifit.huami.com', + 'app_version': '4.3.0-play', + 'source': 'com.huami.watch.hmwatchmanager', + 'country_code': None, + 'device_id': None, + 'third_name': None, + 'lang': 'en', + 'device_model': 'android_phone', + 'allow_registration': 'false', + 'app_name': 'com.huami.midong', + 'code': None, + 'grant_type': None + }, + 'devices': { + 'apptoken': None + }, + 'agps': { + 'apptoken': None + }, + 'data_short': { + 'apptoken': None, + 'startDay': None, + 'endDay': None + }, + 'logout': { + 'login_token': None + }, +} \ No newline at end of file