From a0913cd9c1469795a454d2b2b0d2c0a2d7f583d8 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 6 Jan 2025 14:14:53 -0500 Subject: [PATCH 1/2] feat: add product v4 and downloading code --- roborock/containers.py | 42 ++++++++++--------- roborock/version_1_apis/roborock_client_v1.py | 7 ++++ roborock/web_api.py | 40 +++++++++++++----- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/roborock/containers.py b/roborock/containers.py index 3bda790..7513856 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -881,26 +881,28 @@ class RoborockProductSpec(RoborockBase): @dataclass class RoborockProduct(RoborockBase): - id: int - name: str - model: str - packagename: str - ssid: str - picurl: str - cardpicurl: str - medium_cardpicurl: str - resetwifipicurl: str - resetwifitext: dict - tuyaid: str - status: int - rriotid: str - cardspec: str - pictures: list - nc_mode: str - scope: None - product_tags: list - agreements: list - plugin_pic_url: None + id: int | None = None + name: str | None = None + model: str | None = None + packagename: str | None = None + ssid: str | None = None + picurl: str | None = None + cardpicurl: str | None = None + mediumCardpicurl: str | None = None + resetwifipicurl: str | None = None + configPicUrl: str | None = None + pluginPicUrl: str | None = None + resetwifitext: dict | None = None + tuyaid: str | None = None + status: int | None = None + rriotid: str | None = None + pictures: list | None = None + ncMode: str | None = None + scope: str | None = None + product_tags: list | None = None + agreements: list | None = None + cardspec: str | None = None + plugin_pic_url: str | None = None products_specification: RoborockProductSpec | None = None def __post_init__(self): diff --git a/roborock/version_1_apis/roborock_client_v1.py b/roborock/version_1_apis/roborock_client_v1.py index 279721e..544c27c 100644 --- a/roborock/version_1_apis/roborock_client_v1.py +++ b/roborock/version_1_apis/roborock_client_v1.py @@ -363,6 +363,7 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None: try: self._last_device_msg_in = self.time_func() for data in messages: + self._logger.debug(f"Got message: {data}") protocol = data.protocol if data.payload and protocol in [ RoborockMessageProtocol.RPC_RESPONSE, @@ -418,6 +419,12 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None: consumable = Consumable.from_dict(value) for listener in self.listener_model.protocol_handlers.get(data_protocol, []): listener(consumable) + else: + self._logger.warning( + f"Unknown data protocol {data_point_number}, please create an " + f"issue on the python-roborock repository" + ) + self._logger.info(data) return except ValueError: self._logger.warning( diff --git a/roborock/web_api.py b/roborock/web_api.py index 44646f6..d79fc7d 100644 --- a/roborock/web_api.py +++ b/roborock/web_api.py @@ -54,7 +54,7 @@ async def _get_base_url(self) -> str: raise RoborockMissingParameters( "You are missing parameters for this request, are you sure you " "entered your username?" ) - raise RoborockUrlException(response.get("error")) + raise RoborockUrlException(f"error code: {response_code} msg: {response.get('error')}") response_data = response.get("data") if response_data is None: raise RoborockUrlException("response does not have 'data'") @@ -276,7 +276,7 @@ async def get_products(self, user_data: UserData) -> ProductResponse: product_request = PreparedRequest(base_url, {"header_clientid": header_clientid}) product_response = await product_request.request( "get", - "/api/v3/product", + "/api/v4/product", headers={"Authorization": user_data.token}, ) if product_response is None: @@ -288,24 +288,44 @@ async def get_products(self, user_data: UserData) -> ProductResponse: return ProductResponse.from_dict(result) raise RoborockException("product result was an unexpected type") + async def download_code(self, user_data: UserData, product_id: int): + base_url = await self._get_base_url() + header_clientid = self._get_header_client_id() + product_request = PreparedRequest(base_url, {"header_clientid": header_clientid}) + request = {"apilevel": 99999, "productids": [product_id], "type": 2} + response = await product_request.request( + "post", + "/api/v1/appplugin", + json=request, + headers={"Authorization": user_data.token, "Content-Type": "application/json"}, + ) + return response["data"][0]["url"] + + async def download_category_code(self, user_data: UserData): + base_url = await self._get_base_url() + header_clientid = self._get_header_client_id() + product_request = PreparedRequest(base_url, {"header_clientid": header_clientid}) + response = await product_request.request( + "get", + "api/v1/plugins?apiLevel=99999&type=2", + headers={ + "Authorization": user_data.token, + }, + ) + return {r["category"]: r["url"] for r in response["data"]["categoryPluginList"]} + class PreparedRequest: def __init__(self, base_url: str, base_headers: dict | None = None) -> None: self.base_url = base_url self.base_headers = base_headers or {} - async def request(self, method: str, url: str, params=None, data=None, headers=None) -> dict: + async def request(self, method: str, url: str, params=None, data=None, headers=None, json=None) -> dict: _url = "/".join(s.strip("/") for s in [self.base_url, url]) _headers = {**self.base_headers, **(headers or {})} async with aiohttp.ClientSession() as session: try: - async with session.request( - method, - _url, - params=params, - data=data, - headers=_headers, - ) as resp: + async with session.request(method, _url, params=params, data=data, headers=_headers, json=json) as resp: return await resp.json() except ContentTypeError as err: """If we get an error, lets log everything for debugging.""" From 84b0035febbf5e0c9c13b0e61dfb519f357a0d28 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 6 Jan 2025 14:22:08 -0500 Subject: [PATCH 2/2] fix: remove got message --- roborock/version_1_apis/roborock_client_v1.py | 1 - 1 file changed, 1 deletion(-) diff --git a/roborock/version_1_apis/roborock_client_v1.py b/roborock/version_1_apis/roborock_client_v1.py index 544c27c..b29a465 100644 --- a/roborock/version_1_apis/roborock_client_v1.py +++ b/roborock/version_1_apis/roborock_client_v1.py @@ -363,7 +363,6 @@ def on_message_received(self, messages: list[RoborockMessage]) -> None: try: self._last_device_msg_in = self.time_func() for data in messages: - self._logger.debug(f"Got message: {data}") protocol = data.protocol if data.payload and protocol in [ RoborockMessageProtocol.RPC_RESPONSE,