kopia lustrzana https://codeberg.org/argrento/huami-token
commit
0ee549105d
|
@ -0,0 +1,3 @@
|
|||
notebooks
|
||||
.idea
|
||||
.DS_Store
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Kirill Snezhko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,97 @@
|
|||
# Huami-token
|
||||
|
||||
Script to obtain watch or band bluetooth acess token from Huami servers
|
||||
|
||||
## About
|
||||
|
||||
To use new versions of Amazfit and Xiaomi watches and bands with Gadgetbridge you need special unique key.
|
||||
Read more here: https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Server-Pairing.
|
||||
|
||||
## Preparation
|
||||
|
||||
1. Ensure that you login in Amazfit App with Amazfit or Xiaomi account --
|
||||
because only this login methods are supported. If not, create new Amazfit account
|
||||
with e-mail and password.
|
||||
2. Pair, sync and update your watch with Amazfit App. Your pairing key will be stored on
|
||||
Huami servers.
|
||||
3. Clone this repo:
|
||||
```git clone https://github.com/argrento/huami-token.git```
|
||||
|
||||
## Logging in with Amazfit account
|
||||
Run script with your cridentials: `python huami_token.py --method amazfit --email youemail@example.com --password your_password`.
|
||||
|
||||
Sample output:
|
||||
```bash
|
||||
> python huami_token.py --method amazfit --email my_email --password password
|
||||
Getting access token with amazfit login method...
|
||||
Token: ['UaFHW53RJVYwqXaa7ncPQ']
|
||||
Logging in...
|
||||
Logged in! User id: 1234567890
|
||||
Getting linked wearables...
|
||||
|
||||
Device 1. Mac = AB:CD:EF:12:34:56, auth_key = 0xa3c10e34e5c14637eea6b9efc061069
|
||||
|
||||
Logged out.
|
||||
```
|
||||
|
||||
Here the `auth_key` is the unique pairing key for your watch.
|
||||
|
||||
### Logging in with Xiaomi account
|
||||
This is a little bit harder to use, since you need to login manually on the Xiaomi web site.
|
||||
|
||||
1. Run script `python huami_token.py --method xiaomi`.
|
||||
2. Script will ask you to open Xiaomi login web page. 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
|
||||
3. Login with your credentials there.
|
||||
4. If your login is successful, browser will show the error that connection is not secured.
|
||||
On this stage address will look like this: `https://hm.xiaomi.com/watch.do?code=ALSG_CLOUDSRV_9B8D87D0EB77C71B45FF73B2266D922B`.
|
||||
5. Copy this address.
|
||||
6. Return to script, paste this address and press `enter`.
|
||||
|
||||
Sample output:
|
||||
```bash
|
||||
> python huami_token.py --method xiaomi
|
||||
Getting access token with xiaomi login method...
|
||||
Copy this URL to web-browser
|
||||
|
||||
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
|
||||
|
||||
and login to your Mi account.
|
||||
|
||||
Paste URL after redirection here.
|
||||
https://hm.xiaomi.com/watch.do?code=ALSG_CLOUDSRV_9B8D87D0EB77C71B45FF73B2266D922B
|
||||
Token: ['ALSG_CLOUDSRV_9B8D87D0EB77C71B45FF73B2266D922B']
|
||||
Logging in...
|
||||
Logged in! User id: 3000654321
|
||||
Getting linked wearables...
|
||||
|
||||
Device 1. Mac = 12:34:56:AB:CD:EF, auth_key = 0x3c10e34e5c1463527579996fa83e6d
|
||||
Device 2. Mac = BA:DC:FE:21:43:65, auth_key = 0x0
|
||||
|
||||
Logged out.
|
||||
```
|
||||
|
||||
Here the `auth_key` is the unique pairing key for your watch.
|
||||
|
||||
_In this example I have two devices: the first one is my Amazfit Bip S watch,
|
||||
the second one is my Xiaomi Mi Smart Scale._
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Python 3.7.7
|
||||
* argparse
|
||||
* requests
|
||||
* urllib
|
||||
* random
|
||||
* uuid
|
||||
* json
|
||||
|
||||
## Versioning
|
||||
|
||||
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/your/project/tags).
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
|
||||
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import requests
|
||||
import urllib
|
||||
import random
|
||||
import uuid
|
||||
import json
|
||||
|
||||
|
||||
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.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 get_access_token(self):
|
||||
print("Getting access token with {} login method...".format(self.method))
|
||||
|
||||
if self.method == 'xiaomi':
|
||||
login_url = "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"
|
||||
|
||||
print("Copy this URL to web-browser \n\n{}\n\nand login to your Mi account.".format(login_url))
|
||||
|
||||
token_url = input("\nPaste URL after redirection here.\n")
|
||||
|
||||
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:
|
||||
raise ValueError("No 'code' parameter in login url.")
|
||||
|
||||
self.access_token = token_url_parameters['code']
|
||||
self.country_code = 'US'
|
||||
|
||||
elif self.method == 'amazfit':
|
||||
|
||||
auth_url = 'https://api-user.huami.com/registrations/{}/tokens'.format(urllib.parse.quote(self.email))
|
||||
data = {
|
||||
'state': 'REDIRECTION',
|
||||
'client_id': 'HuaMi',
|
||||
'password': self.password,
|
||||
'redirect_uri': 'https://s3-us-west-2.amazonws.com/hm-registration/successsignin.html',
|
||||
'region': 'us-west-2',
|
||||
'token': 'access',
|
||||
'country_code': 'US'
|
||||
|
||||
}
|
||||
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("Wrong E-mail or Password. Error: {}".format(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']
|
||||
|
||||
print("Token: {}".format(self.access_token))
|
||||
return self.access_token
|
||||
|
||||
def login(self, external_token=None):
|
||||
print("Logging in...")
|
||||
if external_token:
|
||||
self.access_token = external_token
|
||||
|
||||
login_url = 'https://account.huami.com/v2/client/login'
|
||||
data = {
|
||||
'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:4.3.0-play:100152',
|
||||
'country_code': self.country_code,
|
||||
'device_id': self.device_id,
|
||||
'third_name': 'huami' if self.method == 'amazfit' else 'mi-watch',
|
||||
'lang': 'en',
|
||||
'device_model': 'android_phone',
|
||||
'allow_registration': 'false',
|
||||
'app_name': 'com.huami.midong',
|
||||
'code': self.access_token,
|
||||
'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("Login error. Error: {}".format(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))
|
||||
|
||||
def get_wearable_auth_keys(self):
|
||||
print("Getting linked wearables...\n")
|
||||
|
||||
devices_url = "https://api-mifit-us2.huami.com/users/{}/devices".format(urllib.parse.quote(self.user_id))
|
||||
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']
|
||||
|
||||
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 '0')
|
||||
|
||||
print("Device {}. Mac = {}, auth_key = {}".format(
|
||||
idx+1,
|
||||
mac_address,
|
||||
auth_key))
|
||||
|
||||
def logout(self):
|
||||
logout_url = "https://account-us2.huami.com/v1/client/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.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Obtain Bluetooth Auth key from Amazfit servers")
|
||||
parser.add_argument("-m",
|
||||
"--method",
|
||||
choices=["amazfit", "xiaomi"],
|
||||
default="amazfit",
|
||||
required=True,
|
||||
help="Login method ")
|
||||
parser.add_argument("-e",
|
||||
"--email",
|
||||
required=False,
|
||||
help="Account e-mail address")
|
||||
parser.add_argument("-p",
|
||||
"--password",
|
||||
required=False,
|
||||
help="Account Password")
|
||||
args = parser.parse_args()
|
||||
|
||||
device = HuamiAmazfit(method=args.method,
|
||||
email=args.email,
|
||||
password=args.password)
|
||||
device.get_access_token()
|
||||
device.login()
|
||||
device.get_wearable_auth_keys()
|
||||
device.logout()
|
Ładowanie…
Reference in New Issue