python-roborock 2.14.0__tar.gz → 2.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {python_roborock-2.14.0 → python_roborock-2.16.0}/PKG-INFO +3 -3
  2. {python_roborock-2.14.0 → python_roborock-2.16.0}/README.md +2 -2
  3. {python_roborock-2.14.0 → python_roborock-2.16.0}/pyproject.toml +1 -1
  4. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_1_apis/roborock_client_v1.py +3 -3
  5. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/web_api.py +36 -26
  6. {python_roborock-2.14.0 → python_roborock-2.16.0}/LICENSE +0 -0
  7. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/__init__.py +0 -0
  8. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/api.py +0 -0
  9. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/cli.py +0 -0
  10. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/cloud_api.py +0 -0
  11. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/code_mappings.py +0 -0
  12. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/command_cache.py +0 -0
  13. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/const.py +0 -0
  14. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/containers.py +0 -0
  15. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/exceptions.py +0 -0
  16. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/local_api.py +0 -0
  17. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/protocol.py +0 -0
  18. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/py.typed +0 -0
  19. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/roborock_future.py +0 -0
  20. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/roborock_message.py +0 -0
  21. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/roborock_typing.py +0 -0
  22. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/util.py +0 -0
  23. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_1_apis/__init__.py +0 -0
  24. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
  25. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
  26. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_a01_apis/__init__.py +0 -0
  27. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
  28. {python_roborock-2.14.0 → python_roborock-2.16.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-roborock
3
- Version: 2.14.0
3
+ Version: 2.16.0
4
4
  Summary: A package to control Roborock vacuums.
5
5
  Home-page: https://github.com/humbertogontijo/python-roborock
6
6
  License: GPL-3.0-only
@@ -49,7 +49,7 @@ Install this via pip (or your favourite package manager):
49
49
 
50
50
  ## Functionality
51
51
 
52
- You can see all of the commands supported [here]("https://python-roborock.readthedocs.io/en/latest/api_commands.html")
52
+ You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
53
53
 
54
54
  ## Sending Commands
55
55
 
@@ -98,7 +98,7 @@ asyncio.run(main())
98
98
  ## Supported devices
99
99
 
100
100
  You can find what devices are supported
101
- [here]("https://python-roborock.readthedocs.io/en/latest/supported_devices.html").
101
+ [here](https://python-roborock.readthedocs.io/en/latest/supported_devices.html).
102
102
  Please note this may not immediately contain the latest devices.
103
103
 
104
104
 
@@ -18,7 +18,7 @@ Install this via pip (or your favourite package manager):
18
18
 
19
19
  ## Functionality
20
20
 
21
- You can see all of the commands supported [here]("https://python-roborock.readthedocs.io/en/latest/api_commands.html")
21
+ You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
22
22
 
23
23
  ## Sending Commands
24
24
 
@@ -67,7 +67,7 @@ asyncio.run(main())
67
67
  ## Supported devices
68
68
 
69
69
  You can find what devices are supported
70
- [here]("https://python-roborock.readthedocs.io/en/latest/supported_devices.html").
70
+ [here](https://python-roborock.readthedocs.io/en/latest/supported_devices.html).
71
71
  Please note this may not immediately contain the latest devices.
72
72
 
73
73
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-roborock"
3
- version = "2.14.0"
3
+ version = "2.16.0"
4
4
  description = "A package to control Roborock vacuums."
5
5
  authors = ["humbertogontijo <humbertogontijo@users.noreply.github.com>"]
6
6
  license = "GPL-3.0-only"
@@ -107,9 +107,9 @@ class AttributeCache:
107
107
  raise err
108
108
  return self._value
109
109
 
110
- async def async_value(self):
110
+ async def async_value(self, force: bool = False):
111
111
  async with self._mutex:
112
- if self._value is None:
112
+ if self._value is None or force:
113
113
  return await self.task.reset()
114
114
  return self._value
115
115
 
@@ -175,7 +175,7 @@ class RoborockClientV1(RoborockClient, ABC):
175
175
  return self._status_type
176
176
 
177
177
  async def get_status(self) -> Status:
178
- data = self._status_type.from_dict(await self.cache[CacheableAttribute.status].async_value())
178
+ data = self._status_type.from_dict(await self.cache[CacheableAttribute.status].async_value(force=True))
179
179
  if data is None:
180
180
  return self._status_type()
181
181
  return data
@@ -30,16 +30,19 @@ _LOGGER = logging.getLogger(__name__)
30
30
 
31
31
 
32
32
  class RoborockApiClient:
33
- def __init__(self, username: str, base_url=None) -> None:
33
+ def __init__(self, username: str, base_url=None, session: aiohttp.ClientSession | None = None) -> None:
34
34
  """Sample API Client."""
35
35
  self._username = username
36
36
  self._default_url = "https://euiot.roborock.com"
37
37
  self.base_url = base_url
38
38
  self._device_identifier = secrets.token_urlsafe(16)
39
+ if session is None:
40
+ session = aiohttp.ClientSession()
41
+ self.session = session
39
42
 
40
43
  async def _get_base_url(self) -> str:
41
44
  if not self.base_url:
42
- url_request = PreparedRequest(self._default_url)
45
+ url_request = PreparedRequest(self._default_url, self.session)
43
46
  response = await url_request.request(
44
47
  "post",
45
48
  "/api/v1/getUrlByEmail",
@@ -113,7 +116,7 @@ class RoborockApiClient:
113
116
  ):
114
117
  raise RoborockException("Your userdata is missing critical attributes.")
115
118
  base_url = user_data.rriot.r.a
116
- prepare_request = PreparedRequest(base_url)
119
+ prepare_request = PreparedRequest(base_url, self.session)
117
120
  hid = await self._get_home_id(user_data)
118
121
 
119
122
  data = FormData()
@@ -151,7 +154,7 @@ class RoborockApiClient:
151
154
  ):
152
155
  raise RoborockException("Your userdata is missing critical attributes.")
153
156
  base_url = user_data.rriot.r.a
154
- add_device_request = PreparedRequest(base_url)
157
+ add_device_request = PreparedRequest(base_url, self.session)
155
158
 
156
159
  add_device_response = await add_device_request.request(
157
160
  "GET",
@@ -176,7 +179,7 @@ class RoborockApiClient:
176
179
  async def request_code(self) -> None:
177
180
  base_url = await self._get_base_url()
178
181
  header_clientid = self._get_header_client_id()
179
- code_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
182
+ code_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
180
183
 
181
184
  code_response = await code_request.request(
182
185
  "post",
@@ -201,7 +204,7 @@ class RoborockApiClient:
201
204
  base_url = await self._get_base_url()
202
205
  header_clientid = self._get_header_client_id()
203
206
 
204
- login_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
207
+ login_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
205
208
  login_response = await login_request.request(
206
209
  "post",
207
210
  "/api/v1/login",
@@ -239,7 +242,7 @@ class RoborockApiClient:
239
242
  base_url = await self._get_base_url()
240
243
  header_clientid = self._get_header_client_id()
241
244
 
242
- login_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
245
+ login_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
243
246
  login_response = await login_request.request(
244
247
  "post",
245
248
  "/api/v1/loginWithCode",
@@ -270,7 +273,7 @@ class RoborockApiClient:
270
273
  async def _get_home_id(self, user_data: UserData):
271
274
  base_url = await self._get_base_url()
272
275
  header_clientid = self._get_header_client_id()
273
- home_id_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
276
+ home_id_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
274
277
  home_id_response = await home_id_request.request(
275
278
  "get",
276
279
  "/api/v1/getHomeDetail",
@@ -296,6 +299,7 @@ class RoborockApiClient:
296
299
  raise RoborockException("Missing field 'a' in rriot reference")
297
300
  home_request = PreparedRequest(
298
301
  rriot.r.a,
302
+ self.session,
299
303
  {
300
304
  "Authorization": self._get_hawk_authentication(rriot, f"/user/homes/{str(home_id)}"),
301
305
  },
@@ -319,6 +323,7 @@ class RoborockApiClient:
319
323
  raise RoborockException("Missing field 'a' in rriot reference")
320
324
  home_request = PreparedRequest(
321
325
  rriot.r.a,
326
+ self.session,
322
327
  {
323
328
  "Authorization": self._get_hawk_authentication(rriot, "/v2/user/homes/" + str(home_id)),
324
329
  },
@@ -362,6 +367,7 @@ class RoborockApiClient:
362
367
  raise RoborockException("Missing field 'a' in rriot reference")
363
368
  room_request = PreparedRequest(
364
369
  rriot.r.a,
370
+ self.session,
365
371
  {
366
372
  "Authorization": self._get_hawk_authentication(rriot, "/v2/user/homes/" + str(home_id)),
367
373
  },
@@ -386,6 +392,7 @@ class RoborockApiClient:
386
392
  raise RoborockException("Missing field 'a' in rriot reference")
387
393
  scenes_request = PreparedRequest(
388
394
  rriot.r.a,
395
+ self.session,
389
396
  {
390
397
  "Authorization": self._get_hawk_authentication(rriot, f"/user/scene/device/{str(device_id)}"),
391
398
  },
@@ -407,6 +414,7 @@ class RoborockApiClient:
407
414
  raise RoborockException("Missing field 'a' in rriot reference")
408
415
  execute_scene_request = PreparedRequest(
409
416
  rriot.r.a,
417
+ self.session,
410
418
  {
411
419
  "Authorization": self._get_hawk_authentication(rriot, f"/user/scene/{str(scene_id)}/execute"),
412
420
  },
@@ -419,7 +427,7 @@ class RoborockApiClient:
419
427
  """Gets all products and their schemas, good for determining status codes and model numbers."""
420
428
  base_url = await self._get_base_url()
421
429
  header_clientid = self._get_header_client_id()
422
- product_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
430
+ product_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
423
431
  product_response = await product_request.request(
424
432
  "get",
425
433
  "/api/v4/product",
@@ -437,7 +445,7 @@ class RoborockApiClient:
437
445
  async def download_code(self, user_data: UserData, product_id: int):
438
446
  base_url = await self._get_base_url()
439
447
  header_clientid = self._get_header_client_id()
440
- product_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
448
+ product_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
441
449
  request = {"apilevel": 99999, "productids": [product_id], "type": 2}
442
450
  response = await product_request.request(
443
451
  "post",
@@ -450,7 +458,7 @@ class RoborockApiClient:
450
458
  async def download_category_code(self, user_data: UserData):
451
459
  base_url = await self._get_base_url()
452
460
  header_clientid = self._get_header_client_id()
453
- product_request = PreparedRequest(base_url, {"header_clientid": header_clientid})
461
+ product_request = PreparedRequest(base_url, self.session, {"header_clientid": header_clientid})
454
462
  response = await product_request.request(
455
463
  "get",
456
464
  "api/v1/plugins?apiLevel=99999&type=2",
@@ -462,25 +470,27 @@ class RoborockApiClient:
462
470
 
463
471
 
464
472
  class PreparedRequest:
465
- def __init__(self, base_url: str, base_headers: dict | None = None) -> None:
473
+ def __init__(self, base_url: str, session: aiohttp.ClientSession, base_headers: dict | None = None) -> None:
466
474
  self.base_url = base_url
467
475
  self.base_headers = base_headers or {}
476
+ self.session = session
468
477
 
469
478
  async def request(self, method: str, url: str, params=None, data=None, headers=None, json=None) -> dict:
470
479
  _url = "/".join(s.strip("/") for s in [self.base_url, url])
471
480
  _headers = {**self.base_headers, **(headers or {})}
472
- async with aiohttp.ClientSession() as session:
481
+ try:
482
+ async with self.session.request(
483
+ method, _url, params=params, data=data, headers=_headers, json=json
484
+ ) as resp:
485
+ return await resp.json()
486
+ except ContentTypeError as err:
487
+ """If we get an error, lets log everything for debugging."""
473
488
  try:
474
- async with session.request(method, _url, params=params, data=data, headers=_headers, json=json) as resp:
475
- return await resp.json()
476
- except ContentTypeError as err:
477
- """If we get an error, lets log everything for debugging."""
478
- try:
479
- resp_json = await resp.json(content_type=None)
480
- _LOGGER.info("Resp: %s", resp_json)
481
- except ContentTypeError as err_2:
482
- _LOGGER.info(err_2)
483
- resp_raw = await resp.read()
484
- _LOGGER.info("Resp raw: %s", resp_raw)
485
- # Still raise the err so that it's clear it failed.
486
- raise err
489
+ resp_json = await resp.json(content_type=None)
490
+ _LOGGER.info("Resp: %s", resp_json)
491
+ except ContentTypeError as err_2:
492
+ _LOGGER.info(err_2)
493
+ resp_raw = await resp.read()
494
+ _LOGGER.info("Resp raw: %s", resp_raw)
495
+ # Still raise the err so that it's clear it failed.
496
+ raise err