python-roborock 2.2.3__tar.gz → 2.4.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.2.3 → python_roborock-2.4.0}/PKG-INFO +1 -1
  2. {python_roborock-2.2.3 → python_roborock-2.4.0}/pyproject.toml +1 -1
  3. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/code_mappings.py +101 -2
  4. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/const.py +4 -1
  5. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/containers.py +17 -4
  6. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/web_api.py +24 -8
  7. {python_roborock-2.2.3 → python_roborock-2.4.0}/LICENSE +0 -0
  8. {python_roborock-2.2.3 → python_roborock-2.4.0}/README.md +0 -0
  9. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/__init__.py +0 -0
  10. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/api.py +0 -0
  11. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/cli.py +0 -0
  12. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/cloud_api.py +0 -0
  13. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/command_cache.py +0 -0
  14. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/exceptions.py +0 -0
  15. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/local_api.py +0 -0
  16. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/protocol.py +0 -0
  17. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/py.typed +0 -0
  18. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/roborock_future.py +0 -0
  19. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/roborock_message.py +0 -0
  20. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/roborock_typing.py +0 -0
  21. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/util.py +0 -0
  22. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_1_apis/__init__.py +0 -0
  23. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
  24. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
  25. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
  26. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_a01_apis/__init__.py +0 -0
  27. {python_roborock-2.2.3 → python_roborock-2.4.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
  28. {python_roborock-2.2.3 → python_roborock-2.4.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.2.3
3
+ Version: 2.4.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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-roborock"
3
- version = "2.2.3"
3
+ version = "2.4.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"
@@ -4,6 +4,7 @@ import logging
4
4
  from enum import Enum, IntEnum
5
5
 
6
6
  _LOGGER = logging.getLogger(__name__)
7
+ completed_warnings = set()
7
8
 
8
9
 
9
10
  class RoborockEnum(IntEnum):
@@ -16,10 +17,16 @@ class RoborockEnum(IntEnum):
16
17
  @classmethod
17
18
  def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum:
18
19
  if hasattr(cls, "unknown"):
19
- _LOGGER.warning(f"Missing {cls.__name__} code: {key} - defaulting to 'unknown'")
20
+ warning = f"Missing {cls.__name__} code: {key} - defaulting to 'unknown'"
21
+ if warning not in completed_warnings:
22
+ completed_warnings.add(warning)
23
+ _LOGGER.warning(warning)
20
24
  return cls.unknown # type: ignore
21
25
  default_value = next(item for item in cls)
22
- _LOGGER.warning(f"Missing {cls.__name__} code: {key} - defaulting to {default_value}")
26
+ warning = f"Missing {cls.__name__} code: {key} - defaulting to {default_value}"
27
+ if warning not in completed_warnings:
28
+ completed_warnings.add(warning)
29
+ _LOGGER.warning(warning)
23
30
  return default_value
24
31
 
25
32
  @classmethod
@@ -248,7 +255,10 @@ class RoborockFanSpeedP10(RoborockFanPowerCode):
248
255
 
249
256
  class RoborockFanSpeedS8MaxVUltra(RoborockFanPowerCode):
250
257
  off = 105
258
+ quiet = 101
251
259
  balanced = 102
260
+ turbo = 103
261
+ max = 104
252
262
  custom = 106
253
263
  max_plus = 108
254
264
  smart_mode = 110
@@ -324,6 +334,7 @@ class RoborockMopIntensityS8MaxVUltra(RoborockMopIntensityCode):
324
334
  low = 201
325
335
  medium = 202
326
336
  high = 203
337
+ custom = 204
327
338
  max = 208
328
339
  smart_mode = 209
329
340
  custom_water_flow = 207
@@ -395,6 +406,7 @@ class RoborockDockWashTowelModeCode(RoborockEnum):
395
406
  light = 0
396
407
  balanced = 1
397
408
  deep = 2
409
+ smart = 10
398
410
 
399
411
 
400
412
  class RoborockCategory(Enum):
@@ -410,6 +422,93 @@ class RoborockCategory(Enum):
410
422
  return RoborockCategory.UNKNOWN
411
423
 
412
424
 
425
+ class RoborockFinishReason(RoborockEnum):
426
+ manual_interrupt = 21 # Cleaning interrupted by user
427
+ cleanup_interrupted = 24 # Cleanup interrupted
428
+ manual_interrupt_2 = 21
429
+ breakpoint = 32 # Could not continue cleaning
430
+ breakpoint_2 = 33
431
+ cleanup_interrupted_2 = 34
432
+ manual_interrupt_3 = 35
433
+ manual_interrupt_4 = 36
434
+ manual_interrupt_5 = 37
435
+ manual_interrupt_6 = 43
436
+ locate_fail = 45 # Positioning Failed
437
+ cleanup_interrupted_3 = 64
438
+ locate_fail_2 = 65
439
+ manual_interrupt_7 = 48
440
+ manual_interrupt_8 = 49
441
+ manual_interrupt_9 = 50
442
+ cleanup_interrupted_4 = 51
443
+ finished_cleaning = 52 # Finished cleaning
444
+ finished_cleaning_2 = 54
445
+ finished_cleaning_3 = 55
446
+ finished_cleaning_4 = 56
447
+ finished_clenaing_5 = 57
448
+ manual_interrupt_10 = 60
449
+ area_unreachable = 61 # Area unreachable
450
+ area_unreachable_2 = 62
451
+ washing_error = 67 # Washing error
452
+ back_to_wash_failure = 68 # Failed to return to the dock
453
+ cleanup_interrupted_5 = 101
454
+ breakpoint_4 = 102
455
+ manual_interrupt_11 = 103
456
+ cleanup_interrupted_6 = 104
457
+ cleanup_interrupted_7 = 105
458
+ cleanup_interrupted_8 = 106
459
+ cleanup_interrupted_9 = 107
460
+ cleanup_interrupted_10 = 109
461
+ cleanup_interrupted_11 = 110
462
+ patrol_success = 114 # Cruise completed
463
+ patrol_fail = 115 # Cruise failed
464
+ pet_patrol_success = 116 # Pet found
465
+ pet_patrol_fail = 117 # Pet found failed
466
+
467
+
468
+ class RoborockInCleaning(RoborockEnum):
469
+ complete = 0
470
+ global_clean_not_complete = 1
471
+ zone_clean_not_complete = 2
472
+ segment_clean_not_complete = 3
473
+
474
+
475
+ class RoborockCleanType(RoborockEnum):
476
+ all_zone = 1
477
+ draw_zone = 2
478
+ select_zone = 3
479
+ quick_build = 4
480
+ video_patrol = 5
481
+ pet_patrol = 6
482
+
483
+
484
+ class RoborockStartType(RoborockEnum):
485
+ button = 1
486
+ app = 2
487
+ schedule = 3
488
+ mi_home = 4
489
+ quick_start = 5
490
+ voice_control = 13
491
+ routines = 101
492
+ alexa = 801
493
+ google = 802
494
+ ifttt = 803
495
+ yandex = 804
496
+ homekit = 805
497
+ xiaoai = 806
498
+ tmall_genie = 807
499
+ duer = 808
500
+ dingdong = 809
501
+ siri = 810
502
+ clova = 811
503
+ wechat = 901
504
+ alipay = 902
505
+ aqara = 903
506
+ hisense = 904
507
+ huawei = 905
508
+ widget_launch = 820
509
+ smart_watch = 821
510
+
511
+
413
512
  class DyadSelfCleanMode(RoborockEnum):
414
513
  self_clean = 1
415
514
  self_clean_and_dry = 2
@@ -42,8 +42,11 @@ ROBOROCK_1S = "roborock.vacuum.m1s"
42
42
  ROBOROCK_C1 = "roborock.vacuum.c1"
43
43
  ROBOROCK_S8_PRO_ULTRA = "roborock.vacuum.a70"
44
44
  ROBOROCK_S8 = "roborock.vacuum.a51"
45
- ROBOROCK_P10 = "roborock.vacuum.a75"
45
+ ROBOROCK_P10 = "roborock.vacuum.a75" # also known as q_revo
46
46
  ROBOROCK_S8_MAXV_ULTRA = "roborock.vacuum.a97"
47
+ ROBOROCK_QREVO_S = "roborock.vacuum.a104"
48
+ ROBOROCK_QREVO_PRO = "roborock.vacuum.a101"
49
+ ROBOROCK_QREVO_MAXV = "roborock.vacuum.a87"
47
50
 
48
51
  ROBOROCK_DYAD_AIR = "roborock.wetdryvac.a107"
49
52
  ROBOROCK_DYAD_PRO_COMBO = "roborock.wetdryvac.a83"
@@ -13,6 +13,7 @@ from dacite import Config, from_dict
13
13
 
14
14
  from .code_mappings import (
15
15
  RoborockCategory,
16
+ RoborockCleanType,
16
17
  RoborockDockDustCollectionModeCode,
17
18
  RoborockDockErrorCode,
18
19
  RoborockDockTypeCode,
@@ -25,6 +26,8 @@ from .code_mappings import (
25
26
  RoborockFanSpeedS7,
26
27
  RoborockFanSpeedS7MaxV,
27
28
  RoborockFanSpeedS8MaxVUltra,
29
+ RoborockFinishReason,
30
+ RoborockInCleaning,
28
31
  RoborockMopIntensityCode,
29
32
  RoborockMopIntensityP10,
30
33
  RoborockMopIntensityS5Max,
@@ -36,6 +39,7 @@ from .code_mappings import (
36
39
  RoborockMopModeS7,
37
40
  RoborockMopModeS8MaxVUltra,
38
41
  RoborockMopModeS8ProUltra,
42
+ RoborockStartType,
39
43
  RoborockStateCode,
40
44
  )
41
45
  from .const import (
@@ -47,6 +51,9 @@ from .const import (
47
51
  ROBOROCK_G10S_PRO,
48
52
  ROBOROCK_P10,
49
53
  ROBOROCK_Q7_MAX,
54
+ ROBOROCK_QREVO_MAXV,
55
+ ROBOROCK_QREVO_PRO,
56
+ ROBOROCK_QREVO_S,
50
57
  ROBOROCK_S4_MAX,
51
58
  ROBOROCK_S5_MAX,
52
59
  ROBOROCK_S6,
@@ -408,7 +415,7 @@ class Status(RoborockBase):
408
415
  square_meter_clean_area: float | None = None
409
416
  error_code: RoborockErrorCode | None = None
410
417
  map_present: int | None = None
411
- in_cleaning: int | None = None
418
+ in_cleaning: RoborockInCleaning | None = None
412
419
  in_returning: int | None = None
413
420
  in_fresh_state: int | None = None
414
421
  lab_status: int | None = None
@@ -574,6 +581,12 @@ ModelStatus: dict[str, type[Status]] = {
574
581
  ROBOROCK_S8_PRO_ULTRA: S8ProUltraStatus,
575
582
  ROBOROCK_G10S_PRO: S7MaxVStatus,
576
583
  ROBOROCK_P10: P10Status,
584
+ # These likely are not correct,
585
+ # but i am currently unable to do my typical reverse engineering/ get any data from users on this,
586
+ # so this will be here in the mean time.
587
+ ROBOROCK_QREVO_S: P10Status,
588
+ ROBOROCK_QREVO_MAXV: P10Status,
589
+ ROBOROCK_QREVO_PRO: P10Status,
577
590
  ROBOROCK_S8_MAXV_ULTRA: S8MaxvUltraStatus,
578
591
  }
579
592
 
@@ -613,9 +626,9 @@ class CleanRecord(RoborockBase):
613
626
  square_meter_area: float | None = None
614
627
  error: int | None = None
615
628
  complete: int | None = None
616
- start_type: int | None = None
617
- clean_type: int | None = None
618
- finish_reason: int | None = None
629
+ start_type: RoborockStartType | None = None
630
+ clean_type: RoborockCleanType | None = None
631
+ finish_reason: RoborockFinishReason | None = None
619
632
  dust_collection_status: int | None = None
620
633
  avoid_count: int | None = None
621
634
  wash_count: int | None = None
@@ -3,11 +3,13 @@ from __future__ import annotations
3
3
  import base64
4
4
  import hashlib
5
5
  import hmac
6
+ import logging
6
7
  import math
7
8
  import secrets
8
9
  import time
9
10
 
10
11
  import aiohttp
12
+ from aiohttp import ContentTypeError
11
13
 
12
14
  from roborock.containers import HomeData, HomeDataRoom, ProductResponse, RRiot, UserData
13
15
  from roborock.exceptions import (
@@ -23,6 +25,8 @@ from roborock.exceptions import (
23
25
  RoborockUrlException,
24
26
  )
25
27
 
28
+ _LOGGER = logging.getLogger(__name__)
29
+
26
30
 
27
31
  class RoborockApiClient:
28
32
  def __init__(self, username: str, base_url=None) -> None:
@@ -294,11 +298,23 @@ class PreparedRequest:
294
298
  _url = "/".join(s.strip("/") for s in [self.base_url, url])
295
299
  _headers = {**self.base_headers, **(headers or {})}
296
300
  async with aiohttp.ClientSession() as session:
297
- async with session.request(
298
- method,
299
- _url,
300
- params=params,
301
- data=data,
302
- headers=_headers,
303
- ) as resp:
304
- return await resp.json()
301
+ try:
302
+ async with session.request(
303
+ method,
304
+ _url,
305
+ params=params,
306
+ data=data,
307
+ headers=_headers,
308
+ ) as resp:
309
+ return await resp.json()
310
+ except ContentTypeError as err:
311
+ """If we get an error, lets log everything for debugging."""
312
+ try:
313
+ resp_json = await resp.json(content_type=None)
314
+ _LOGGER.info("Resp: %s", resp_json)
315
+ except ContentTypeError as err_2:
316
+ _LOGGER.info(err_2)
317
+ resp_raw = await resp.read()
318
+ _LOGGER.info("Resp raw: %s", resp_raw)
319
+ # Still raise the err so that it's clear it failed.
320
+ raise err
File without changes