diff --git a/midealocal/cli.py b/midealocal/cli.py index cdaed028..6853390f 100644 --- a/midealocal/cli.py +++ b/midealocal/cli.py @@ -17,6 +17,7 @@ from midealocal.cloud import ( SUPPORTED_CLOUDS, MideaCloud, + get_default_cloud, get_midea_cloud, get_preset_account_cloud, ) @@ -55,9 +56,10 @@ async def _get_cloud(self) -> MideaCloud: or not self.namespace.password ): default_cloud = get_preset_account_cloud() + default_cloud_name = get_default_cloud() _LOGGER.info("Using preset account.") return get_midea_cloud( - cloud_name=default_cloud["cloud_name"], + cloud_name=default_cloud_name, session=self.session, account=default_cloud["username"], password=default_cloud["password"], @@ -195,6 +197,10 @@ async def download(self) -> None: lua = await cloud.download_lua(str(Path()), device_type, device_sn, model) _LOGGER.info("Downloaded lua file: %s", lua) + _LOGGER.debug("Download plugin file for %s [%s]", device_sn, hex(device_type)) + plugin = await cloud.download_plugin(str(Path()), device_type, device_sn) + _LOGGER.info("Downloaded plugin file: %s", plugin) + async def set_attribute(self) -> None: """Set attribute for device.""" device_list = await self.discover() diff --git a/midealocal/cloud.py b/midealocal/cloud.py index bb1626fa..e112710c 100644 --- a/midealocal/cloud.py +++ b/midealocal/cloud.py @@ -209,7 +209,7 @@ async def _api_request( ) raw = await r.read() _LOGGER.debug( - "Midea cloud API url: %s, data: %s, response: %s", + "Midea cloud API url: %s, \n data: %s, \n response: %s", url, _redact_data(str(data)), _redact_data(str(raw)), @@ -306,6 +306,15 @@ async def download_lua( """Download lua integration.""" raise NotImplementedError + async def download_plugin( + self, + path: str, + device_type: int, + sn: str, + ) -> str | None: + """Download lua integration.""" + raise NotImplementedError + class MeijuCloud(MideaCloud): """Meiju Cloud.""" @@ -333,6 +342,20 @@ def __init__( api_url=cloud_data["api_url"], ) + def _make_general_data(self) -> dict[str, Any]: + return { + "src": self._app_id, + "format": "2", + "stamp": datetime.now(tz=UTC).strftime("%Y%m%d%H%M%S"), + "platformId": "1", + "deviceId": self._device_id, + "reqId": token_hex(16), + "uid": self._uid, + "clientType": "1", + "appId": self._app_id, + "language": "en_US", + } + async def login(self) -> bool: """Authenticate to Meiju Cloud.""" if login_id := await self._get_login_id(): @@ -539,6 +562,46 @@ async def download_lua( await fp.write(stream) return str(fnm) if fnm else None + async def download_plugin( + self, + path: str, + device_type: int, + sn: str, + ) -> str | None: + """Download lua integration.""" + data = self._make_general_data() + data.update( + { + "clientVersion": "201", + "match": "1", + "applianceList": [ + { + "appModel": sn[9:17], + "appType": hex(device_type), + "modelNumber": "0", + }, + ], + }, + ) + fnm = None + if response := await self._api_request( + endpoint="/v1/plugin/update/getplugin", + data=data, + ): + # get file name from url + _LOGGER.debug("response: %s, type: %s", response, type(response)) + file_name = response["list"][0]["url"].split("/")[-1] + # download plugin from url + res = await self._session.get(response["list"][0]["url"]) + if res.status == HTTPStatus.OK: + # get the file content in binary mode + plugin = await res.read() + if plugin: + fnm = f"{path}/{file_name}" + async with aiofiles.open(fnm, "wb") as fp: + await fp.write(plugin) + return str(fnm) if fnm else None + class SmartHomeCloud(MideaCloud): """MSmart Home Cloud.""" @@ -582,6 +645,7 @@ def _make_general_data(self) -> dict[str, Any]: "uid": self._uid, "clientType": "1", "appId": self._app_id, + "language": "en_US", } async def _api_request( @@ -699,20 +763,18 @@ async def download_lua( manufacturer_code: str = "0000", ) -> str | None: """Download lua integration.""" - data = { - "clientType": "1", - "appId": self._app_id, - "format": "2", - "deviceId": self._device_id, - "iotAppId": self._app_id, - "applianceMFCode": manufacturer_code, - "applianceType": hex(device_type), - "applianceSn": self._security.aes_encrypt_with_fixed_key( - sn.encode("ascii"), - ).hex(), - "version": "0", - "encryptedType ": "2", - } + data = self._make_general_data() + data.update( + { + "applianceMFCode": manufacturer_code, + "applianceType": hex(device_type), + "applianceSn": self._security.aes_encrypt_with_fixed_key( + sn.encode("ascii"), + ).hex(), + "version": "0", + "encryptedType ": "2", + }, + ) if model_number is not None: data["modelNumber"] = model_number fnm = None @@ -734,6 +796,45 @@ async def download_lua( await fp.write(stream) return str(fnm) if fnm else None + async def download_plugin( + self, + path: str, + device_type: int, + sn: str, + ) -> str | None: + """Download lua integration.""" + data = self._make_general_data() + data.update( + { + "clientVersion": "0", + "applianceList": [ + { + "appModel": sn[9:17], + "appType": hex(device_type), + "modelNumber": "0", + }, + ], + }, + ) + fnm = None + if response := await self._api_request( + endpoint="/v1/plugin/update/overseas/get", + data=data, + ): + # get file name from url + _LOGGER.debug("response: %s, type: %s", response, type(response)) + file_name = response["result"][0]["url"].split("/")[-1] + # download plugin from url + res = await self._session.get(response["result"][0]["url"]) + if res.status == HTTPStatus.OK: + # get the file content in binary mode + plugin = await res.read() + if plugin: + fnm = f"{path}/{file_name}" + async with aiofiles.open(fnm, "wb") as fp: + await fp.write(plugin) + return str(fnm) if fnm else None + class MideaAirCloud(MideaCloud): """Midea Air Cloud."""