From a40b8c9bbe4be1b474e86112fcfd1fd6466dafa0 Mon Sep 17 00:00:00 2001 From: vanous Date: Sun, 16 May 2021 01:18:18 +0200 Subject: [PATCH] add UIHH aGPS packing --- .gitignore | 1 + buildozer.spec | 4 +-- huami_token.py | 17 +++++------ main.py | 76 ++++++++++++++++++++++++++++++++++++++++++-------- typemap.json | 7 +++++ typemap.py | 24 ++++++++++++++++ 6 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 typemap.json create mode 100644 typemap.py diff --git a/.gitignore b/.gitignore index 506d2fa..8fe302a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ cep_alm_pak.zip cep_7days.zip credentials.* README.html +tmp # C extensions *.so diff --git a/buildozer.spec b/buildozer.spec index 4bdd15e..1d1078f 100644 --- a/buildozer.spec +++ b/buildozer.spec @@ -13,7 +13,7 @@ package.domain = cz.linux source.dir = . # (list) Source files to include (let empty to include all the files) -source.include_exts = py,png,jpg,kv,atlas +source.include_exts = py,png,jpg,kv,atlas,json # (list) List of inclusions using pattern matching #source.include_patterns = assets/*,images/*.png @@ -28,7 +28,7 @@ source.exclude_dirs = tests, bin, screenshots #source.exclude_patterns = license,images/*/*.jpg # (str) Application versioning (method 1) -version = 0.17 +version = 0.18 # (str) Application versioning (method 2) # version.regex = __version__ = ['"](.*)['"] diff --git a/huami_token.py b/huami_token.py index 5675235..1f5a3bf 100644 --- a/huami_token.py +++ b/huami_token.py @@ -159,24 +159,25 @@ class HuamiAmazfit: return devices_dict - def get_gps_data(self): - agps_packs = ["AGPS_ALM", "AGPSZIP"] - agps_file_names = ["cep_alm_pak.zip", "cep_7days.zip"] + def get_gps_data(self) -> None: + """Download GPS packs: almanac and AGPS""" + agps_packs = ["AGPS_ALM", "AGPSZIP", "LLE", "AGPS"] + agps_file_names = ["cep_1week.zip", "cep_7days.zip", "lle_1week.zip", "cep_pak.bin"] 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)) + for pack_idx, agps_pack_name in enumerate(agps_packs): + print(f"Downloading {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) + with requests.get(agps_result['fileUrl'], stream=True) as request: + with open(agps_file_names[pack_idx], 'wb') as gps_file: + shutil.copyfileobj(request.raw, gps_file) def logout(self): logout_url = urls.URLS['logout'] diff --git a/main.py b/main.py index 8b45612..c87ffb6 100644 --- a/main.py +++ b/main.py @@ -124,15 +124,15 @@ class Main(App): self.fetch_agps_button.bind(on_press=self.on_press_button_agps) self.fetch_agps_button.disabled=True - share_agps_button = MyButton(text='Share aGPS') - share_agps_button.bind(on_press=self.on_press_button_share_agps) + create_uihh_file_button = MyButton(text='Create UIHH') + create_uihh_file_button.bind(on_press=self.create_uihh_agps_file) buttons_layout.add_widget(self.get_token_button) #buttons_layout.add_widget(login_button) buttons_layout.add_widget(self.fetch_key_button) buttons_layout.add_widget(self.fetch_agps_button) #sharing doesn't work - #buttons_layout.add_widget(share_agps_button) + buttons_layout.add_widget(create_uihh_file_button) self.paste_token_input_layout = BoxLayout(orientation="horizontal", spacing=SPACING) paste_token_input_label=MyLabel(text='URL result') @@ -428,6 +428,8 @@ class Main(App): def get_agps_files(self, *xargs): import zipfile + import shutil + import os try: res=self.huamidevice.login() if res: @@ -439,25 +441,75 @@ class Main(App): self.huamidevice.get_gps_data() agps_file_names = ["cep_alm_pak.zip"] + agps_file_names = ["cep_1week.zip", "cep_7days.zip", "lle_1week.zip", "cep_pak.bin"] + data_dir="./tmp" 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) + debug_print(data_dir) + for filename in agps_file_names: + sdpathfile = os.path.join(data_dir, filename) + shutil.copyfile(filename, sdpathfile) + print(f"process {filename}") + if "zip" not in filename: + continue + with zipfile.ZipFile(filename, "r") as zip_f: + #zip_f.extractall() + zip_f.extractall(data_dir) self.instructions_label.text="Files downloaded and extracted" #Clock.schedule_once(partial(self.doit), 1) + def create_uihh_agps_file(self, *xargs): + import typemap as tm + import pathlib + from binascii import crc32 + data_dir="./tmp" + + if ( platform == 'android' ): + from jnius import autoclass + from jnius import cast + Environment = autoclass('android.os.Environment') + File = autoclass('java.io.File') + data_dir = Environment.getExternalStorageDirectory().getPath() + + content = b"" + + for typeID, inputfilename in tm.typemap.items(): + fullPathName = pathlib.Path(data_dir).joinpath(inputfilename) + if not fullPathName.is_file(): + self.instructions_label.text=f"[E] File not found: {fullPathName}" + return + + with open(fullPathName, "rb") as f: + filecontent = f.read() + + print(f"[I] Packing {inputfilename}") + fileheader = chr(1).encode() +typeID.to_bytes(1,"big") + len(filecontent).to_bytes(4,"little") + crc32(filecontent).to_bytes(4,"little") + content += fileheader + filecontent + + print("[I] Adding header") + header = ["UIHH" , chr(0x04) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x01) , crc32(content).to_bytes(4,"little") , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , len(content).to_bytes(4,"little") , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00) , chr(0x00)] + + merged_header=b"" + for i in header: + if isinstance(i, str): + i=i.encode() + merged_header+=i + + content = merged_header+content + + + outputfile = pathlib.Path(data_dir).joinpath("aGPS_UIHH.bin") + print(f"[I] Writing {outputfile}") + with open(outputfile, "wb") as f: + f.write(content) + + self.instructions_label.text="aGPS UIHH created" + #Clock.schedule_once(partial(self.doit), 1) def on_press_button_share_agps(self, instance): #not working because android broke it if ( platform == 'android' ): diff --git a/typemap.json b/typemap.json new file mode 100644 index 0000000..fb362c0 --- /dev/null +++ b/typemap.json @@ -0,0 +1,7 @@ +{ "0x05" : "gps_alm.bin", + "0x0f" : "gln_alm.bin", + "0x86" : "lle_bds.lle", + "0x87" : "lle_gps.lle", + "0x88" : "lle_glo.lle", + "0x89" : "lle_gal.lle", + "0x8a" : "lle_qzss.lle"} diff --git a/typemap.py b/typemap.py new file mode 100644 index 0000000..ce88db2 --- /dev/null +++ b/typemap.py @@ -0,0 +1,24 @@ +#Copyright (C) 2021 Andreas Shimokawa +# +#This file is part of Gadgetbridge-tools. +# +#Gadgetbridge is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published +#by the Free Software Foundation, either version 3 of the License, or +#(at your option) any later version. +# +#Gadgetbridge is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU Affero General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . + +import json + +with open("typemap.json") as f: + jsonmap = json.load(f) + +typemap={int(k,16):v for k,v in jsonmap.items()} +