Skip to content

Commit e8c2fb6

Browse files
committed
fix: cycle through iot urls
1 parent ed46bce commit e8c2fb6

File tree

2 files changed

+48
-27
lines changed

2 files changed

+48
-27
lines changed

roborock/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,7 @@ class RoborockTooManyRequest(RoborockException):
7777

7878
class RoborockRateLimit(RoborockException):
7979
"""Class for our rate limits exceptions."""
80+
81+
82+
class RoborockNoResponseFromBaseURL(RoborockException):
83+
"""We could not find an url that had a record of the given account."""

roborock/web_api.py

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@
2222
RoborockInvalidEmail,
2323
RoborockInvalidUserAgreement,
2424
RoborockMissingParameters,
25+
RoborockNoResponseFromBaseURL,
2526
RoborockNoUserAgreement,
2627
RoborockRateLimit,
2728
RoborockTooFrequentCodeRequests,
28-
RoborockTooManyRequest,
29-
RoborockUrlException,
3029
)
3130

3231
_LOGGER = logging.getLogger(__name__)
@@ -52,37 +51,49 @@ class RoborockApiClient:
5251
def __init__(self, username: str, base_url=None, session: aiohttp.ClientSession | None = None) -> None:
5352
"""Sample API Client."""
5453
self._username = username
55-
self._default_url = "https://euiot.roborock.com"
5654
self.base_url = base_url
5755
self._device_identifier = secrets.token_urlsafe(16)
5856
self.session = session
57+
self._country = None
58+
self._country_code = None
5959

6060
async def _get_base_url(self) -> str:
6161
if not self.base_url:
62-
url_request = PreparedRequest(self._default_url, self.session)
63-
response = await url_request.request(
64-
"post",
65-
"/api/v1/getUrlByEmail",
66-
params={"email": self._username, "needtwostepauth": "false"},
62+
for iot_url in [
63+
"https://usiot.roborock.com",
64+
"https://euiot.roborock.com",
65+
"https://cniot.roborock.com",
66+
"https://ruiot.roborock.com",
67+
]:
68+
url_request = PreparedRequest(iot_url, self.session)
69+
response = await url_request.request(
70+
"post",
71+
"/api/v1/getUrlByEmail",
72+
params={"email": self._username, "needtwostepauth": "false"},
73+
)
74+
if response is None:
75+
continue
76+
response_code = response.get("code")
77+
if response_code != 200:
78+
if response_code == 2003:
79+
raise RoborockInvalidEmail("Your email was incorrectly formatted.")
80+
elif response_code == 1001:
81+
raise RoborockMissingParameters(
82+
"You are missing parameters for this request, are you sure you entered your username?"
83+
)
84+
else:
85+
_LOGGER.warning(
86+
"Failed to get base url for %s with the following context: %s", self._username, response
87+
)
88+
if response["data"]["countrycode"] is not None:
89+
self._country_code = response["data"]["countrycode"]
90+
self._country = response["data"]["country"]
91+
self.base_url = response["data"]["url"]
92+
return self.base_url
93+
raise RoborockNoResponseFromBaseURL(
94+
"No account was found for any base url we tried. Either your email is incorrect or we do not have a"
95+
" record of the roborock server your device is on."
6796
)
68-
if response is None:
69-
raise RoborockUrlException("get url by email returned None")
70-
response_code = response.get("code")
71-
if response_code != 200:
72-
_LOGGER.info("Get base url failed for %s with the following context: %s", self._username, response)
73-
if response_code == 2003:
74-
raise RoborockInvalidEmail("Your email was incorrectly formatted.")
75-
elif response_code == 1001:
76-
raise RoborockMissingParameters(
77-
"You are missing parameters for this request, are you sure you entered your username?"
78-
)
79-
elif response_code == 9002:
80-
raise RoborockTooManyRequest("Please temporarily disable making requests and try again later.")
81-
raise RoborockUrlException(f"error code: {response_code} msg: {response.get('error')}")
82-
response_data = response.get("data")
83-
if response_data is None:
84-
raise RoborockUrlException("response does not have 'data'")
85-
self.base_url = response_data.get("url")
8697
return self.base_url
8798

8899
def _get_header_client_id(self):
@@ -249,14 +260,20 @@ async def _sign_key_v3(self, s: str) -> str:
249260

250261
return code_response["data"]["k"]
251262

252-
async def code_login_v4(self, code: int | str, country: str, country_code: int) -> UserData:
263+
async def code_login_v4(
264+
self, code: int | str, country: str | None = None, country_code: int | None = None
265+
) -> UserData:
253266
"""
254267
Login via code authentication.
255268
:param code: The code from the email.
256269
:param country: The two-character representation of the country, i.e. "US"
257270
:param country_code: the country phone number code i.e. 1 for US.
258271
"""
259272
base_url = await self._get_base_url()
273+
if country is None:
274+
country = self._country
275+
if country_code is None:
276+
country_code = self._country_code
260277
header_clientid = self._get_header_client_id()
261278
x_mercy_ks = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(16))
262279
x_mercy_k = await self._sign_key_v3(x_mercy_ks)

0 commit comments

Comments
 (0)