Skip to content

Commit 22241fc

Browse files
committed
feat: Iterate possible iot domains on 3030 error
This error is returned by Roborock's API when there is an account currently in deletion. This can happen e.g. if a user erronously creates their account in the wrong region and then deletes it so they can create a new one in the correct region.
1 parent 3468b05 commit 22241fc

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

roborock/web_api.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ def __init__(
7474
self._device_identifier = secrets.token_urlsafe(16)
7575
self.session = session
7676
self._iot_login_info: IotLoginInfo | None = None
77+
self._base_urls = BASE_URLS if base_url is None else [base_url]
7778

7879
async def _get_iot_login_info(self) -> IotLoginInfo:
7980
if self._iot_login_info is None:
80-
valid_urls = BASE_URLS if self._base_url is None else [self._base_url]
81-
for iot_url in valid_urls:
81+
for iot_url in self._base_urls:
8282
url_request = PreparedRequest(iot_url, self.session)
8383
response = await url_request.request(
8484
"post",
@@ -269,6 +269,10 @@ async def request_code_v4(self) -> None:
269269
raise RoborockAccountDoesNotExist("Account does not exist - check your login and try again.")
270270
elif response_code == 9002:
271271
raise RoborockTooFrequentCodeRequests("You have attempted to request too many codes. Try again later")
272+
elif response_code == 3030 and len(self._base_urls) > 1:
273+
self._base_urls = self._base_urls[1:]
274+
self._iot_login_info = None
275+
return await self.request_code_v4()
272276
else:
273277
raise RoborockException(f"{code_response.get('msg')} - response code: {code_response.get('code')}")
274278

tests/test_web_api.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ async def test_url_cycling(mock_rest) -> None:
9292
"""Test that we cycle through the URLs correctly."""
9393
# Clear mock rest so that we can override the patches.
9494
mock_rest.clear()
95-
# 1. Mock US URL to return valid status but None for countrycode
9695

96+
# 1. Mock US URL to return valid status but None for countrycode
9797
mock_rest.post(
9898
re.compile("https://usiot.roborock.com/api/v1/getUrlByEmail.*"),
9999
status=200,
@@ -184,6 +184,80 @@ async def test_url_cycling(mock_rest) -> None:
184184
assert len(mock_rest.requests) == 3
185185

186186

187+
async def test_thirty_thirty_cycling(mock_rest) -> None:
188+
"""Test that we cycle through the URLs correctly when users have deleted accounts in higher prio regions."""
189+
# Clear mock rest so that we can override the patches.
190+
mock_rest.clear()
191+
192+
mock_rest.post(
193+
re.compile("https://usiot.roborock.com/api/v1/getUrlByEmail.*"),
194+
status=200,
195+
payload={
196+
"code": 200,
197+
"data": {"url": "https://usiot.roborock.com", "country": "US", "countrycode": 1},
198+
"msg": "Account in deletion",
199+
},
200+
)
201+
202+
mock_rest.post(
203+
re.compile("https://euiot.roborock.com/api/v1/getUrlByEmail.*"),
204+
status=200,
205+
payload={
206+
"code": 200,
207+
"data": {"url": "https://euiot.roborock.com", "country": "EU", "countrycode": 49},
208+
"msg": "Success",
209+
},
210+
)
211+
212+
mock_rest.post(
213+
re.compile("https://usiot.roborock.com/api/v4/email/code/send.*"),
214+
status=200,
215+
payload={
216+
"code": 3030,
217+
},
218+
)
219+
mock_rest.post(
220+
re.compile("https://euiot.roborock.com/api/v4/email/code/send.*"),
221+
status=200,
222+
payload={
223+
"code": 200,
224+
},
225+
)
226+
227+
mock_rest.post(re.compile("https://ruiot.roborock.com/api/v1/getUrlByEmail.*"), status=500)
228+
mock_rest.post(re.compile("https://cniot.roborock.com/api/v1/getUrlByEmail.*"), status=500)
229+
230+
client = RoborockApiClient("test@example.com")
231+
await client.request_code_v4()
232+
233+
print(mock_rest.requests)
234+
assert (
235+
len(
236+
mock_rest.requests[
237+
(
238+
"post",
239+
normalize_url("https://euiot.roborock.com/api/v4/email/code/send"),
240+
)
241+
]
242+
)
243+
== 1
244+
)
245+
assert (
246+
len(
247+
mock_rest.requests[
248+
(
249+
"post",
250+
normalize_url("https://usiot.roborock.com/api/v4/email/code/send"),
251+
)
252+
]
253+
)
254+
== 1
255+
)
256+
# Assert that we didn't try on the Russian or Chinese regions
257+
assert "https://ruiot.roborock.com/api/v4/email/code/send" not in mock_rest.requests
258+
assert "https://cniot.roborock.com/api/v4/email/code/send" not in mock_rest.requests
259+
260+
187261
async def test_missing_country_login(mock_rest) -> None:
188262
"""Test that we cycle through the URLs correctly."""
189263
mock_rest.clear()

0 commit comments

Comments
 (0)