From 0a542013f9a5cec6b3c8bf6f1ea28bf8f4f8837d Mon Sep 17 00:00:00 2001 From: Thomas Luther Date: Fri, 9 Feb 2024 13:36:49 +0000 Subject: [PATCH] export_system code cleanup and fixes api preparation for globalization --- README.md | 4 +- api/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 157 bytes api/__pycache__/api.cpython-311.pyc | Bin 0 -> 60655 bytes api/__pycache__/errors.cpython-311.pyc | Bin 0 -> 5349 bytes api/api.py | 51 +++++++++++++++-------- export_system.py | 48 +++++++++++---------- 6 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 api/__pycache__/__init__.cpython-311.pyc create mode 100644 api/__pycache__/api.cpython-311.pyc create mode 100644 api/__pycache__/errors.cpython-311.pyc diff --git a/README.md b/README.md index abf0d54..b513e6e 100644 --- a/README.md +++ b/README.md @@ -143,4 +143,6 @@ Pull requests are the best way to propose changes to the codebase. # Showing Your Appreciation -If you like this project, please give it a star on [GitHub](https://github.com/thomluther/anker-solix-api) \ No newline at end of file +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/thomasluthe) + +If you like this project, please give it a star on [GitHub](https://github.com/thomluther/anker-solix-api) diff --git a/api/__pycache__/__init__.cpython-311.pyc b/api/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66f030388ce830cbc96f1af94e2a8a9ffd5ae1d8 GIT binary patch literal 157 zcmZ3^%ge<81kNmnQbF`%5CH>>P{wCAAY(d13PUi1CZpdlIY~;;_lhPbtkwwJTx; Y8UV7Qm>)=dU}j`w{J;PsikN|701@FMj{pDw literal 0 HcmV?d00001 diff --git a/api/__pycache__/api.cpython-311.pyc b/api/__pycache__/api.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c634e0f227c0232f98de52919c4bfcd24be3bb93 GIT binary patch literal 60655 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 literal 0 HcmV?d00001 diff --git a/api/api.py b/api/api.py index be9d44f..f8b6218 100644 --- a/api/api.py +++ b/api/api.py @@ -15,7 +15,6 @@ import logging import os import sys import time -from typing import Optional from aiohttp import ClientSession from aiohttp.client_exceptions import ClientError @@ -29,7 +28,11 @@ from . import errors _LOGGER: logging.Logger = logging.getLogger(__name__) """Default definitions required for the Anker Power/Solix Cloud API""" -_API_BASE: str = "https://ankerpower-api-eu.anker.com" +# API servers per region. Country assignment not clear, defaulting to EU server +_API_SERVERS = { + "eu": "https://ankerpower-api-eu.anker.com", + "com": "https://ankerpower-api.anker.com", +} _API_LOGIN = "passport/login" _API_HEADERS = { "Content-Type": "application/json", @@ -37,6 +40,10 @@ _API_HEADERS = { "App-Name": "anker_power", "Os-Type": "android", } +_API_COUNTRIES = { + "com": ["US", "CN"], + "eu": ["DE", "IT", "FR", "ES"], +} # TODO(2): Expand list once more ID assignments are known """Following are the Anker Power/Solix Cloud API endpoints known so far""" _API_ENDPOINTS = { @@ -83,6 +90,8 @@ _API_ENDPOINTS = { 'power_service/v1/app/compatible/save_ota_complete_status', 'power_service/v1/app/compatible/check_third_sn', 'power_service/v1/app/compatible/save_compatible_solar', + 'power_service/v1/app/compatible/get_confirm_permissions', + 'power_service/v1/app/compatible/confirm_permissions_settings', 'power_service/v1/app/after_sale/check_popup', 'power_service/v1/app/after_sale/check_sn', 'power_service/v1/app/after_sale/mark_sn', @@ -95,8 +104,6 @@ _API_ENDPOINTS = { 'power_service/v1/app/check_upgrade_record', 'power_service/v1/app/get_upgrade_record', 'power_service/v1/app/get_phonecode_list', - 'power_service/v1/app/compatible/get_confirm_permissions', - 'power_service/v1/app/compatible/confirm_permissions_settings', 'power_service/v1/message_not_disturb', 'power_service/v1/get_message_not_disturb', 'power_service/v1/read_message', @@ -105,6 +112,7 @@ _API_ENDPOINTS = { 'power_service/v1/product_categories', 'power_service/v1/product_accessories', + Structure of the JSON response for an API Login Request: An unexpired token_id must be used for API request, along with the gtoken which is an MD5 hash of the returned(encrypted) user_id. The combination of the provided token and MD5 hashed user_id authenticate the client to the server. @@ -153,10 +161,16 @@ class AnkerSolixApi: logger=None, ) -> None: """Initialize.""" - self._api_base: str = _API_BASE + self._countryId: str = countryId.upper() + self._api_base: str | None = None + for region, countries in _API_COUNTRIES.items(): + if self._countryId in countries: + self._api_base = _API_SERVERS.get(region) + # default to EU server + if not self._api_base: + self._api_base = _API_SERVERS.get("eu") self._email: str = email self._password: str = password - self._countryId: str = countryId.upper() self._session: ClientSession = websession self._loggedIn: bool = False self._testdir: str = "test" @@ -180,13 +194,14 @@ class AnkerSolixApi: self._timezone: str = ( self._getTimezoneGMTString() ) # Timezone format: 'GMT+01:00' - self._gtoken: Optional[str] = None - self._token: Optional[str] = None - self._token_expiration: Optional[datetime] = None - self._login_response: Optional[LOGIN_RESPONSE] = {} + self._gtoken: str | None = None + self._token: str | None = None + self._token_expiration: datetime | None = None + self._login_response: LOGIN_RESPONSE = {} # Define Encryption for password, using ECDH assymetric key exchange for shared secret calculation, which must be used to encrypt the password using AES-256-CBC with seed of 16 - # uncompressed public key from Anker server in the format 04 [32 byte x vlaue] [32 byte y value] + # uncompressed public key from EU Anker server in the format 04 [32 byte x vlaue] [32 byte y value] + # TODO(2): COM Anker server public key usage to be validated self._api_public_key_hex = "04c5c00c4f8d1197cc7c3167c52bf7acb054d722f0ef08dcd7e0883236e0d72a3868d9750cb47fa4619248f3d83f0f662671dadc6e2d31c2f41db0161651c7c076" self._curve = ( ec.SECP256R1() @@ -430,8 +445,8 @@ class AnkerSolixApi: method: str, endpoint: str, *, - headers: Optional[dict] = None, - json: Optional[dict] = None, # lint W0621 + headers: dict | None = None, + json: dict | None = None, ) -> dict: """Handle all requests to the API. This is also called recursively by login requests if necessary.""" if not headers: @@ -495,7 +510,7 @@ class AnkerSolixApi: # Unauthorized or forbidden request if self._retry_attempt: raise errors.AuthorizationError( - "Login failed for user %s" % self._email + f"Login failed for user {self._email}" ) from err self._logger.warning("Login failed, retrying authentication...") if await self.async_authenticate(restart=True): @@ -504,7 +519,7 @@ class AnkerSolixApi: ) self._logger.error("Login failed for user %s", self._email) raise errors.AuthorizationError( - "Login failed for user %s" % self._email + f"Login failed for user {self._email}" ) from err raise ClientError( f"There was an error while requesting {endpoint}: {err}" @@ -525,7 +540,7 @@ class AnkerSolixApi: ) self._logger.error("Login failed for user %s", self._email) raise errors.AuthorizationError( - "Login failed for user %s" % self._email + f"Login failed for user {self._email}" ) from err except errors.AnkerSolixError as err: # Other Exception from API self._logger.error("ANKER API ERROR: %s", err) @@ -819,7 +834,7 @@ class AnkerSolixApi: if toFile: resp = self._saveToFile( os.path.join(self._testdir, f"set_power_cutoff_{deviceSn}.json"), - json=data, + data=data, ) else: resp = await self.request("post", _API_ENDPOINTS["set_cutoff"], json=data) @@ -904,7 +919,7 @@ class AnkerSolixApi: } if toFile: resp = self._saveToFile( - os.path.join(self._testdir, f"set_device_parm_{siteId}.json"), json=data + os.path.join(self._testdir, f"set_device_parm_{siteId}.json"), data=data ) else: resp = await self.request( diff --git a/export_system.py b/export_system.py index a27a7a5..fa20e84 100644 --- a/export_system.py +++ b/export_system.py @@ -19,7 +19,8 @@ import sys import time from aiohttp import ClientSession -from api import api +from aiohttp.client_exceptions import ClientError +from api import api, errors _LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER.addHandler(logging.StreamHandler(sys.stdout)) @@ -112,16 +113,16 @@ def export(filename: str, d: dict = None) -> None: d = {} time.sleep(1) # central delay between multiple requests if len(d) == 0: - CONSOLE.info(f"WARNING: File {filename} not saved because JSON is empty") + CONSOLE.info("WARNING: File %s not saved because JSON is empty", filename) return elif RANDOMIZE: d = check_keys(d) try: with open(filename, "w", encoding="utf-8") as file: json.dump(d, file, indent=2) - CONSOLE.info(f"Saved JSON to file {filename}") - except Exception as err: - CONSOLE.error(f"ERROR: Failed to save JSON to file {filename}: {err}") + CONSOLE.info("Saved JSON to file %s", filename) + except OSError as err: + CONSOLE.error("ERROR: Failed to save JSON to file %s: %s", filename, err) return @@ -169,7 +170,7 @@ async def main() -> bool: # noqa: C901 # first update sites in API object CONSOLE.info("\nQuerying site information...") await myapi.update_sites() - CONSOLE.info(f"Sites: {len(myapi.sites)}, Devices: {len(myapi.devices)}") + CONSOLE.info("Sites: %s, Devices: %s", len(myapi.sites), len(myapi.devices)) _LOGGER.debug(json.dumps(myapi.devices, indent=2)) # Query API using direct endpoints to save full response of each query in json files @@ -212,7 +213,7 @@ async def main() -> bool: # noqa: C901 ), ) # shows only owner devices for siteId, site in myapi.sites.items(): - CONSOLE.info(f"\nExporting site specific data for site {siteId}...") + CONSOLE.info("\nExporting site specific data for site %s...", siteId) CONSOLE.info("Exporting scene info...") export( os.path.join(folder, f"scene_{randomize(siteId,'site_id')}.json"), @@ -247,7 +248,7 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId}, ), ) - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info("Exporting wifi list...") @@ -262,7 +263,7 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info("Exporting site price...") @@ -277,14 +278,14 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info("Exporting device parameter settings...") try: export( os.path.join( - folder, "device_parm_{randomize(siteId,'site_id')}.json" + folder, f"device_parm_{randomize(siteId,'site_id')}.json" ), await myapi.request( "post", @@ -292,12 +293,14 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId, "param_type": "4"}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") for sn, device in myapi.devices.items(): CONSOLE.info( - f"\nExporting device specific data for device {device.get('name','')} SN {sn}..." + "\nExporting device specific data for device %s SN %s...", + device.get("name", ""), + sn, ) siteId = device.get("site_id", "") admin = site.get("is_admin") @@ -313,7 +316,7 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId, "device_sn": sn}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info("Exporting fittings...") @@ -328,7 +331,7 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId, "device_sn": sn}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info("Exporting load...") @@ -341,16 +344,17 @@ async def main() -> bool: # noqa: C901 json={"site_id": siteId, "device_sn": sn}, ), ) # works only for site owners - except Exception: + except (ClientError,errors.AnkerSolixError): if not admin: CONSOLE.warning("Query requires account of site owner!") CONSOLE.info( - f"\nCompleted export of Anker Solix system data for user {USER}" + "\nCompleted export of Anker Solix system data for user %s", USER ) if RANDOMIZE: CONSOLE.info( - f"Folder {os.path.abspath(folder)} contains the randomized JSON files. Pls check and update fields that may contain unrecognized personalized data." + "Folder %s contains the randomized JSON files. Pls check and update fields that may contain unrecognized personalized data.", + os.path.abspath(folder), ) CONSOLE.info( "Following trace or site IDs, SNs and MAC addresses have been randomized in files (from -> to):" @@ -358,12 +362,12 @@ async def main() -> bool: # noqa: C901 CONSOLE.info(json.dumps(RANDOMDATA, indent=2)) else: CONSOLE.info( - f"Folder {os.path.abspath(folder)} contains the JSON files." + "Folder %s contains the JSON files.", os.path.abspath(folder) ) return True - except Exception as err: - CONSOLE.info(f"{type(err)}: {err}") + except (ClientError,errors.AnkerSolixError) as err: + CONSOLE.info("%s: %s", type(err), err) return False @@ -375,4 +379,4 @@ if __name__ == "__main__": except KeyboardInterrupt: CONSOLE.info("Aborted!") except Exception as exception: - CONSOLE.info(f"{type(exception)}: {exception}") + CONSOLE.info("%s: %s", type(exception), exception)