Skip to content

Commit 498bbe2

Browse files
committed
feat(cloud): add plugin download for meiju and smarthome
1 parent e995c50 commit 498bbe2

File tree

2 files changed

+123
-16
lines changed

2 files changed

+123
-16
lines changed

midealocal/cli.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from midealocal.cloud import (
1818
SUPPORTED_CLOUDS,
1919
MideaCloud,
20+
get_default_cloud,
2021
get_midea_cloud,
2122
get_preset_account_cloud,
2223
)
@@ -55,9 +56,10 @@ async def _get_cloud(self) -> MideaCloud:
5556
or not self.namespace.password
5657
):
5758
default_cloud = get_preset_account_cloud()
59+
default_cloud_name = get_default_cloud()
5860
_LOGGER.info("Using preset account.")
5961
return get_midea_cloud(
60-
cloud_name=default_cloud["cloud_name"],
62+
cloud_name=default_cloud_name,
6163
session=self.session,
6264
account=default_cloud["username"],
6365
password=default_cloud["password"],
@@ -195,6 +197,10 @@ async def download(self) -> None:
195197
lua = await cloud.download_lua(str(Path()), device_type, device_sn, model)
196198
_LOGGER.info("Downloaded lua file: %s", lua)
197199

200+
_LOGGER.debug("Download plugin file for %s [%s]", device_sn, hex(device_type))
201+
plugin = await cloud.download_plugin(str(Path()), device_type, device_sn)
202+
_LOGGER.info("Downloaded plugin file: %s", plugin)
203+
198204
async def set_attribute(self) -> None:
199205
"""Set attribute for device."""
200206
device_list = await self.discover()

midealocal/cloud.py

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ async def _api_request(
209209
)
210210
raw = await r.read()
211211
_LOGGER.debug(
212-
"Midea cloud API url: %s, data: %s, response: %s",
212+
"Midea cloud API url: %s, \n data: %s, \n response: %s",
213213
url,
214214
_redact_data(str(data)),
215215
_redact_data(str(raw)),
@@ -306,6 +306,15 @@ async def download_lua(
306306
"""Download lua integration."""
307307
raise NotImplementedError
308308

309+
async def download_plugin(
310+
self,
311+
path: str,
312+
device_type: int,
313+
sn: str,
314+
) -> str | None:
315+
"""Download lua integration."""
316+
raise NotImplementedError
317+
309318

310319
class MeijuCloud(MideaCloud):
311320
"""Meiju Cloud."""
@@ -333,6 +342,20 @@ def __init__(
333342
api_url=cloud_data["api_url"],
334343
)
335344

345+
def _make_general_data(self) -> dict[str, Any]:
346+
return {
347+
"src": self._app_id,
348+
"format": "2",
349+
"stamp": datetime.now(tz=UTC).strftime("%Y%m%d%H%M%S"),
350+
"platformId": "1",
351+
"deviceId": self._device_id,
352+
"reqId": token_hex(16),
353+
"uid": self._uid,
354+
"clientType": "1",
355+
"appId": self._app_id,
356+
"language": "en_US",
357+
}
358+
336359
async def login(self) -> bool:
337360
"""Authenticate to Meiju Cloud."""
338361
if login_id := await self._get_login_id():
@@ -539,6 +562,46 @@ async def download_lua(
539562
await fp.write(stream)
540563
return str(fnm) if fnm else None
541564

565+
async def download_plugin(
566+
self,
567+
path: str,
568+
device_type: int,
569+
sn: str,
570+
) -> str | None:
571+
"""Download lua integration."""
572+
data = self._make_general_data()
573+
data.update(
574+
{
575+
"clientVersion": "201",
576+
"match": "1",
577+
"applianceList": [
578+
{
579+
"appModel": sn[9:17],
580+
"appType": hex(device_type),
581+
"modelNumber": "0",
582+
},
583+
],
584+
},
585+
)
586+
fnm = None
587+
if response := await self._api_request(
588+
endpoint="/v1/plugin/update/getplugin",
589+
data=data,
590+
):
591+
# get file name from url
592+
_LOGGER.debug("response: %s, type: %s", response, type(response))
593+
file_name = response["list"][0]["url"].split("/")[-1]
594+
# download plugin from url
595+
res = await self._session.get(response["list"][0]["url"])
596+
if res.status == HTTPStatus.OK:
597+
# get the file content in binary mode
598+
plugin = await res.read()
599+
if plugin:
600+
fnm = f"{path}/{file_name}"
601+
async with aiofiles.open(fnm, "wb") as fp:
602+
await fp.write(plugin)
603+
return str(fnm) if fnm else None
604+
542605

543606
class SmartHomeCloud(MideaCloud):
544607
"""MSmart Home Cloud."""
@@ -582,6 +645,7 @@ def _make_general_data(self) -> dict[str, Any]:
582645
"uid": self._uid,
583646
"clientType": "1",
584647
"appId": self._app_id,
648+
"language": "en_US",
585649
}
586650

587651
async def _api_request(
@@ -699,20 +763,18 @@ async def download_lua(
699763
manufacturer_code: str = "0000",
700764
) -> str | None:
701765
"""Download lua integration."""
702-
data = {
703-
"clientType": "1",
704-
"appId": self._app_id,
705-
"format": "2",
706-
"deviceId": self._device_id,
707-
"iotAppId": self._app_id,
708-
"applianceMFCode": manufacturer_code,
709-
"applianceType": hex(device_type),
710-
"applianceSn": self._security.aes_encrypt_with_fixed_key(
711-
sn.encode("ascii"),
712-
).hex(),
713-
"version": "0",
714-
"encryptedType ": "2",
715-
}
766+
data = self._make_general_data()
767+
data.update(
768+
{
769+
"applianceMFCode": manufacturer_code,
770+
"applianceType": hex(device_type),
771+
"applianceSn": self._security.aes_encrypt_with_fixed_key(
772+
sn.encode("ascii"),
773+
).hex(),
774+
"version": "0",
775+
"encryptedType ": "2",
776+
},
777+
)
716778
if model_number is not None:
717779
data["modelNumber"] = model_number
718780
fnm = None
@@ -734,6 +796,45 @@ async def download_lua(
734796
await fp.write(stream)
735797
return str(fnm) if fnm else None
736798

799+
async def download_plugin(
800+
self,
801+
path: str,
802+
device_type: int,
803+
sn: str,
804+
) -> str | None:
805+
"""Download lua integration."""
806+
data = self._make_general_data()
807+
data.update(
808+
{
809+
"clientVersion": "0",
810+
"applianceList": [
811+
{
812+
"appModel": sn[9:17],
813+
"appType": hex(device_type),
814+
"modelNumber": "0",
815+
},
816+
],
817+
},
818+
)
819+
fnm = None
820+
if response := await self._api_request(
821+
endpoint="/v1/plugin/update/overseas/get",
822+
data=data,
823+
):
824+
# get file name from url
825+
_LOGGER.debug("response: %s, type: %s", response, type(response))
826+
file_name = response["result"][0]["url"].split("/")[-1]
827+
# download plugin from url
828+
res = await self._session.get(response["result"][0]["url"])
829+
if res.status == HTTPStatus.OK:
830+
# get the file content in binary mode
831+
plugin = await res.read()
832+
if plugin:
833+
fnm = f"{path}/{file_name}"
834+
async with aiofiles.open(fnm, "wb") as fp:
835+
await fp.write(plugin)
836+
return str(fnm) if fnm else None
837+
737838

738839
class MideaAirCloud(MideaCloud):
739840
"""Midea Air Cloud."""

0 commit comments

Comments
 (0)