Skip to content

Commit b00e1d0

Browse files
committed
Add support cloud verify via phone and captcha
1 parent b19e8eb commit b00e1d0

File tree

11 files changed

+89
-52
lines changed

11 files changed

+89
-52
lines changed

custom_components/xiaomi_gateway3/config_flow.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
import logging
23

34
import homeassistant.helpers.config_validation as cv
45
import voluptuous as vol
@@ -11,6 +12,8 @@
1112
from .core.xiaomi_cloud import AuthResult, MiCloud
1213
from .hass import hass_utils
1314

15+
_LOGGER = logging.getLogger(__name__)
16+
1417
SERVERS = {
1518
"cn": "China",
1619
"de": "Europe",
@@ -85,7 +88,7 @@ def _show_cloud_form(self, defaults: dict, errors: dict = None):
8588
errors=errors,
8689
)
8790

88-
async def _process_cloud_result(self, result: AuthResult | None):
91+
async def _process_cloud_result(self, result: AuthResult):
8992
if result["ok"]:
9093
return self.async_create_entry(
9194
title=self.cloud_user_input["username"],
@@ -104,13 +107,15 @@ async def _process_cloud_result(self, result: AuthResult | None):
104107
description_placeholders={"image": image},
105108
)
106109

107-
if email := result.get("email"):
110+
if verify := result.get("verify"):
108111
return self.async_show_form(
109-
step_id="cloud_email",
112+
step_id="cloud_verify",
110113
data_schema=vol_schema({vol.Required("code"): str}),
111-
description_placeholders={"email": email},
114+
description_placeholders={"address": verify},
112115
)
113116

117+
_LOGGER.error("Can't login", exc_info=result["exception"])
118+
114119
return self._show_cloud_form(
115120
self.cloud_user_input, errors={"base": "cant_login"}
116121
)
@@ -136,8 +141,8 @@ async def async_step_cloud_captcha(self, user_input: dict = None):
136141
result = await self.cloud.login_captcha(user_input["code"])
137142
return await self._process_cloud_result(result)
138143

139-
async def async_step_cloud_email(self, user_input: dict = None):
140-
result = await self.cloud.verify_email(user_input["code"])
144+
async def async_step_cloud_verify(self, user_input: dict = None):
145+
result = await self.cloud.login_verify(user_input["code"])
141146
return await self._process_cloud_result(result)
142147

143148
async def async_step_token(self, user_input: dict = None):

custom_components/xiaomi_gateway3/core/xiaomi_cloud.py

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
SERVERS = ["cn", "de", "i2", "ru", "sg", "us"]
1616
SID = "xiaomiio"
1717

18+
FLAG_PHONE = 4
19+
FLAG_EMAIL = 8
20+
1821

1922
class AuthResult(TypedDict, total=False):
2023
ok: bool
2124
captcha: bytes | None
22-
email: str | None
25+
verify: str | None # phone or email address
2326
token: str | None
2427
exception: Exception | None
2528

@@ -54,9 +57,7 @@ async def login(self, username: str, password: str, **kwargs) -> AuthResult:
5457
return {"ok": False, "captcha": data["image"]}
5558

5659
if notification_url := res1.get("notificationUrl"):
57-
data = await self._send_email_ticket(notification_url)
58-
self.auth = {"identity_session": data["identity_session"]}
59-
return {"ok": False, "email": data["email"]}
60+
return await self._get_notification_url(notification_url)
6061

6162
res2 = await self._get_location(res1["location"])
6263

@@ -90,15 +91,23 @@ async def login_token(self, token: str) -> AuthResult:
9091
return {"ok": False, "exception": e}
9192

9293
async def login_captcha(self, code: str) -> AuthResult:
94+
if "flag" in self.auth and "identity_session" in self.auth:
95+
return await self._send_ticket(
96+
self.auth["flag"], self.auth["identity_session"], code
97+
)
98+
9399
return await self.login(
94100
self.auth["username"], self.auth["password"], captcha_code=code
95101
)
96102

97-
async def verify_email(self, ticket: str) -> AuthResult:
103+
async def login_verify(self, ticket: str) -> AuthResult:
104+
flag = self.auth["flag"]
105+
key = "Phone" if flag == FLAG_PHONE else "Email"
106+
98107
r = await self.session.post(
99-
"https://account.xiaomi.com/identity/auth/verifyEmail",
108+
f"https://account.xiaomi.com/identity/auth/verify{key}",
100109
cookies={"identity_session": self.auth["identity_session"]},
101-
params={"_flag": 8, "ticket": ticket, "trust": "false", "_json": "true"},
110+
params={"_flag": flag, "ticket": ticket, "trust": "false", "_json": "true"},
102111
)
103112
res1 = parse_auth_response(await r.read())
104113
assert res1["code"] == 0, res1
@@ -162,33 +171,56 @@ async def _get_captcha_url(self, captcha_url: str) -> dict:
162171
body = await r.read()
163172
return {"image": body, "ick": r.cookies["ick"]}
164173

165-
async def _send_email_ticket(self, notification_url: str) -> dict:
174+
async def _get_notification_url(self, notification_url: str) -> AuthResult:
166175
assert "/identity/authStart" in notification_url, notification_url
167176
notification_url = notification_url.replace("authStart", "list")
168177

169178
r = await self.session.get(notification_url)
170179
res1 = parse_auth_response(await r.read())
171180
assert res1["code"] == 2, res1
172181

173-
identity_session = r.cookies["identity_session"]
182+
flag = res1["flag"]
183+
assert flag in (FLAG_EMAIL, FLAG_PHONE), res1
184+
185+
return await self._send_ticket(flag, r.cookies["identity_session"])
186+
187+
async def _send_ticket(
188+
self, flag: int, identity_session, captcha_code: str = None
189+
) -> AuthResult:
190+
key = "Phone" if flag == FLAG_PHONE else "Email"
174191

175192
r = await self.session.get(
176-
"https://account.xiaomi.com/identity/auth/verifyEmail",
193+
f"https://account.xiaomi.com/identity/auth/verify{key}",
177194
cookies={"identity_session": identity_session},
178-
params={"_flag": 8, "_json": "true"},
195+
params={"_flag": flag, "_json": "true"},
179196
)
180-
res2 = parse_auth_response(await r.read())
181-
assert res2["code"] == 0, res2
197+
res1 = parse_auth_response(await r.read())
198+
assert res1["code"] == 0, res1
199+
200+
if captcha_code:
201+
cookies = {"identity_session": identity_session, "ick": self.auth["ick"]}
202+
data = {"retry": 0, "icode": captcha_code, "_json": "true"}
203+
else:
204+
cookies = {"identity_session": identity_session}
205+
data = {"retry": 0, "icode": "", "_json": "true"}
182206

183207
r = await self.session.post(
184-
"https://account.xiaomi.com/identity/auth/sendEmailTicket",
185-
cookies={"identity_session": identity_session},
186-
data={"retry": 0, "icode": "", "_json": "true"},
208+
f"https://account.xiaomi.com/identity/auth/send{key}Ticket",
209+
cookies=cookies,
210+
data=data,
187211
)
188-
res3 = parse_auth_response(await r.read())
189-
assert res3["code"] == 0, res3
212+
res2 = parse_auth_response(await r.read())
213+
214+
self.auth = {"flag": flag, "identity_session": identity_session}
215+
216+
if captcha_url := res2.get("captchaUrl"):
217+
data = await self._get_captcha_url(captcha_url)
218+
self.auth["ick"] = data["ick"]
219+
return {"ok": False, "captcha": data["image"]}
220+
221+
assert res2["code"] == 0, res2
190222

191-
return {"email": res2["maskedEmail"], "identity_session": identity_session}
223+
return {"ok": False, "verify": res1[f"masked{key}"]}
192224

193225
async def request(self, server: str, path: str, params: dict) -> dict:
194226
form: dict[str, str] = {"data": json.dumps(params, separators=(",", ":"))}

custom_components/xiaomi_gateway3/translations/en.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828
},
2929
"cloud_captcha": {
3030
"title": "Verify account",
31-
"description": "Enter code from image: {image}",
31+
"description": "Enter code from: ![]({image})",
3232
"data": {
3333
"code": "Code"
3434
}
3535
},
36-
"cloud_email": {
36+
"cloud_verify": {
3737
"title": "Verify account",
38-
"description": "Enter code from email: {email}",
38+
"description": "Enter code from: {address}",
3939
"data": {
4040
"code": "Code"
4141
}

custom_components/xiaomi_gateway3/translations/hu.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

custom_components/xiaomi_gateway3/translations/pl.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

custom_components/xiaomi_gateway3/translations/pt-BR.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

custom_components/xiaomi_gateway3/translations/ro.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

custom_components/xiaomi_gateway3/translations/ru.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Проверка аккаунта",
30-
"description": "Введите код с картинки: {image}",
30+
"description": "Введите код из: ![]({image})",
3131
"data": {
3232
"code": "Код"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Проверка аккаунта",
37-
"description": "Введите код из e-mail: {email}",
37+
"description": "Введите код из: {address}",
3838
"data": {
3939
"code": "Код"
4040
}

custom_components/xiaomi_gateway3/translations/ua.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

custom_components/xiaomi_gateway3/translations/zh-Hans.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
},
2828
"cloud_captcha": {
2929
"title": "Verify account",
30-
"description": "Enter code from image: {image}",
30+
"description": "Enter code from: ![]({image})",
3131
"data": {
3232
"code": "Code"
3333
}
3434
},
35-
"cloud_email": {
35+
"cloud_verify": {
3636
"title": "Verify account",
37-
"description": "Enter code from email: {email}",
37+
"description": "Enter code from: {address}",
3838
"data": {
3939
"code": "Code"
4040
}

0 commit comments

Comments
 (0)