python-roborock 2.58.1__tar.gz → 3.10.8__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 (105) hide show
  1. {python_roborock-2.58.1 → python_roborock-3.10.8}/PKG-INFO +29 -26
  2. {python_roborock-2.58.1 → python_roborock-3.10.8}/README.md +28 -25
  3. {python_roborock-2.58.1 → python_roborock-3.10.8}/pyproject.toml +18 -10
  4. python_roborock-3.10.8/roborock/__init__.py +30 -0
  5. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/api.py +1 -1
  6. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/broadcast_protocol.py +1 -1
  7. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/cli.py +76 -40
  8. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/cloud_api.py +1 -1
  9. python_roborock-3.10.8/roborock/data/__init__.py +9 -0
  10. python_roborock-3.10.8/roborock/data/b01_q10/__init__.py +2 -0
  11. python_roborock-3.10.8/roborock/data/b01_q10/b01_q10_code_mappings.py +213 -0
  12. python_roborock-3.10.8/roborock/data/b01_q10/b01_q10_containers.py +53 -0
  13. python_roborock-3.10.8/roborock/data/b01_q7/__init__.py +2 -0
  14. python_roborock-2.58.1/roborock/b01_containers.py → python_roborock-3.10.8/roborock/data/b01_q7/b01_q7_code_mappings.py +1 -130
  15. python_roborock-3.10.8/roborock/data/b01_q7/b01_q7_containers.py +130 -0
  16. python_roborock-3.10.8/roborock/data/code_mappings.py +146 -0
  17. python_roborock-3.10.8/roborock/data/containers.py +450 -0
  18. python_roborock-3.10.8/roborock/data/dyad/__init__.py +2 -0
  19. python_roborock-3.10.8/roborock/data/dyad/dyad_code_mappings.py +102 -0
  20. python_roborock-3.10.8/roborock/data/dyad/dyad_containers.py +28 -0
  21. python_roborock-3.10.8/roborock/data/v1/__init__.py +3 -0
  22. python_roborock-2.58.1/roborock/clean_modes.py → python_roborock-3.10.8/roborock/data/v1/v1_clean_modes.py +6 -2
  23. python_roborock-2.58.1/roborock/code_mappings.py → python_roborock-3.10.8/roborock/data/v1/v1_code_mappings.py +147 -528
  24. python_roborock-2.58.1/roborock/containers.py → python_roborock-3.10.8/roborock/data/v1/v1_containers.py +51 -495
  25. python_roborock-3.10.8/roborock/data/zeo/__init__.py +2 -0
  26. python_roborock-3.10.8/roborock/data/zeo/zeo_code_mappings.py +136 -0
  27. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/device_features.py +26 -3
  28. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/README.md +5 -3
  29. python_roborock-3.10.8/roborock/devices/__init__.py +11 -0
  30. python_roborock-3.10.8/roborock/devices/cache.py +143 -0
  31. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/channel.py +5 -0
  32. python_roborock-3.10.8/roborock/devices/device.py +223 -0
  33. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/device_manager.py +61 -32
  34. python_roborock-3.10.8/roborock/devices/file_cache.py +77 -0
  35. python_roborock-3.10.8/roborock/devices/local_channel.py +290 -0
  36. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/mqtt_channel.py +11 -2
  37. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/a01/__init__.py +3 -3
  38. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/b01/__init__.py +1 -2
  39. python_roborock-3.10.8/roborock/devices/traits/v1/__init__.py +293 -0
  40. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/child_lock.py +11 -2
  41. python_roborock-3.10.8/roborock/devices/traits/v1/clean_summary.py +83 -0
  42. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/command.py +9 -2
  43. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/common.py +30 -7
  44. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/consumeable.py +2 -1
  45. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/device_features.py +15 -12
  46. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/do_not_disturb.py +17 -15
  47. python_roborock-3.10.8/roborock/devices/traits/v1/dust_collection_mode.py +13 -0
  48. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/flow_led_status.py +11 -2
  49. python_roborock-3.10.8/roborock/devices/traits/v1/home.py +262 -0
  50. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/led_status.py +11 -2
  51. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/map_content.py +34 -1
  52. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/maps.py +1 -1
  53. python_roborock-3.10.8/roborock/devices/traits/v1/network_info.py +55 -0
  54. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/rooms.py +1 -1
  55. python_roborock-3.10.8/roborock/devices/traits/v1/routines.py +26 -0
  56. python_roborock-3.10.8/roborock/devices/traits/v1/smart_wash_params.py +13 -0
  57. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/status.py +1 -1
  58. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/valley_electricity_timer.py +16 -12
  59. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/v1/volume.py +1 -0
  60. python_roborock-3.10.8/roborock/devices/traits/v1/wash_towel_mode.py +13 -0
  61. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/v1_channel.py +223 -53
  62. python_roborock-3.10.8/roborock/mqtt/health_manager.py +51 -0
  63. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/mqtt/roborock_session.py +131 -57
  64. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/mqtt/session.py +4 -0
  65. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/protocol.py +1 -1
  66. python_roborock-3.10.8/roborock/protocols/__init__.py +3 -0
  67. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/protocols/v1_protocol.py +50 -6
  68. python_roborock-3.10.8/roborock/py.typed +0 -0
  69. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/roborock_typing.py +1 -1
  70. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_1_apis/roborock_client_v1.py +4 -13
  71. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_1_apis/roborock_local_client_v1.py +2 -10
  72. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_1_apis/roborock_mqtt_client_v1.py +1 -1
  73. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_a01_apis/roborock_client_a01.py +5 -3
  74. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +1 -1
  75. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/web_api.py +27 -1
  76. python_roborock-2.58.1/roborock/__init__.py +0 -39
  77. python_roborock-2.58.1/roborock/devices/__init__.py +0 -7
  78. python_roborock-2.58.1/roborock/devices/cache.py +0 -64
  79. python_roborock-2.58.1/roborock/devices/device.py +0 -105
  80. python_roborock-2.58.1/roborock/devices/local_channel.py +0 -131
  81. python_roborock-2.58.1/roborock/devices/traits/v1/__init__.py +0 -163
  82. python_roborock-2.58.1/roborock/devices/traits/v1/clean_summary.py +0 -29
  83. python_roborock-2.58.1/roborock/devices/traits/v1/home.py +0 -174
  84. python_roborock-2.58.1/roborock/devices/v1_rpc_channel.py +0 -211
  85. {python_roborock-2.58.1 → python_roborock-3.10.8}/.gitignore +0 -0
  86. {python_roborock-2.58.1 → python_roborock-3.10.8}/LICENSE +0 -0
  87. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/callbacks.py +0 -0
  88. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/command_cache.py +0 -0
  89. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/const.py +0 -0
  90. /python_roborock-2.58.1/roborock/py.typed → /python_roborock-3.10.8/roborock/data/zeo/zeo_containers.py +0 -0
  91. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/a01_channel.py +0 -0
  92. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/b01_channel.py +0 -0
  93. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/__init__.py +0 -0
  94. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/devices/traits/traits_mixin.py +0 -0
  95. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/exceptions.py +0 -0
  96. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/map/__init__.py +0 -0
  97. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/map/map_parser.py +0 -0
  98. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/mqtt/__init__.py +0 -0
  99. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/protocols/a01_protocol.py +0 -0
  100. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/protocols/b01_protocol.py +0 -0
  101. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/roborock_future.py +0 -0
  102. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/roborock_message.py +0 -0
  103. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/util.py +0 -0
  104. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_1_apis/__init__.py +0 -0
  105. {python_roborock-2.58.1 → python_roborock-3.10.8}/roborock/version_a01_apis/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 2.58.1
3
+ Version: 3.10.8
4
4
  Summary: A package to control Roborock vacuums.
5
5
  Project-URL: Repository, https://github.com/humbertogontijo/python-roborock
6
6
  Project-URL: Documentation, https://python-roborock.readthedocs.io/
@@ -35,8 +35,12 @@ Description-Content-Type: text/markdown
35
35
  </a>
36
36
  <img src="https://img.shields.io/pypi/pyversions/python-roborock.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
37
37
  <img src="https://img.shields.io/pypi/l/python-roborock.svg?style=flat-square" alt="License">
38
+ <a href="https://codecov.io/github/Python-roborock/python-roborock" >
39
+ <img src="https://codecov.io/github/Python-roborock/python-roborock/graph/badge.svg?token=KEK4S3FPSZ" alt="Code Coverage"/>
40
+ </a>
38
41
  </p>
39
42
 
43
+
40
44
  Roborock library for online and offline control of your vacuums.
41
45
 
42
46
  ## Installation
@@ -49,16 +53,14 @@ Install this via pip (or your favourite package manager):
49
53
 
50
54
  You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
51
55
 
52
- ## Sending Commands
56
+ ## Example Usage
53
57
 
54
- Here is an example that requires no manual intervention and can be done all automatically. You can skip some steps by
55
- caching values or looking at them and grabbing them manually.
56
58
  ```python
57
59
  import asyncio
58
60
 
59
- from roborock import HomeDataProduct, DeviceData, RoborockCommand
60
- from roborock.version_1_apis import RoborockMqttClientV1, RoborockLocalClientV1
61
61
  from roborock.web_api import RoborockApiClient
62
+ from roborock.devices.device_manager import create_device_manager, UserParams
63
+
62
64
 
63
65
  async def main():
64
66
  web_api = RoborockApiClient(username="youremailhere")
@@ -69,30 +71,31 @@ async def main():
69
71
  code = input("What is the code?")
70
72
  user_data = await web_api.code_login(code)
71
73
 
72
- # Get home data
73
- home_data = await web_api.get_home_data_v2(user_data)
74
-
75
- # Get the device you want
76
- device = home_data.devices[0]
77
-
78
- # Get product ids:
79
- product_info: dict[str, HomeDataProduct] = {
80
- product.id: product for product in home_data.products
81
- }
82
- # Create the Mqtt(aka cloud required) Client
83
- device_data = DeviceData(device, product_info[device.product_id].model)
84
- mqtt_client = RoborockMqttClientV1(user_data, device_data)
85
- networking = await mqtt_client.get_networking()
86
- local_device_data = DeviceData(device, product_info[device.product_id].model, networking.ip)
87
- local_client = RoborockLocalClientV1(local_device_data)
88
- # You can use the send_command to send any command to the device
89
- status = await local_client.send_command(RoborockCommand.GET_STATUS)
90
- # Or use existing functions that will give you data classes
91
- status = await local_client.get_status()
74
+ # Create a device manager that can discover devices.
75
+ user_params = UserParams(
76
+ username="youremailhere",
77
+ user_data=user_data,
78
+ )
79
+ device_manager = await create_device_manager(user_params)
80
+ devices = await device_manager.get_devices()
81
+
82
+ # Get all vacuum devices that support the v1 PropertiesApi
83
+ for device in devices:
84
+ if not device.v1_properties:
85
+ continue
86
+
87
+ # Refresh the current device status
88
+ status_trait = device.v1_properties.status
89
+ await status_trait.refresh()
90
+ print(status_trait)
92
91
 
93
92
  asyncio.run(main())
94
93
  ```
95
94
 
95
+ See [examples/example.py](examples/example.py) for a more full featured example
96
+ that has performance improvements to cache cloud information to prefer
97
+ connections over the local network.
98
+
96
99
  ## Supported devices
97
100
 
98
101
  You can find what devices are supported
@@ -6,8 +6,12 @@
6
6
  </a>
7
7
  <img src="https://img.shields.io/pypi/pyversions/python-roborock.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">
8
8
  <img src="https://img.shields.io/pypi/l/python-roborock.svg?style=flat-square" alt="License">
9
+ <a href="https://codecov.io/github/Python-roborock/python-roborock" >
10
+ <img src="https://codecov.io/github/Python-roborock/python-roborock/graph/badge.svg?token=KEK4S3FPSZ" alt="Code Coverage"/>
11
+ </a>
9
12
  </p>
10
13
 
14
+
11
15
  Roborock library for online and offline control of your vacuums.
12
16
 
13
17
  ## Installation
@@ -20,16 +24,14 @@ Install this via pip (or your favourite package manager):
20
24
 
21
25
  You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
22
26
 
23
- ## Sending Commands
27
+ ## Example Usage
24
28
 
25
- Here is an example that requires no manual intervention and can be done all automatically. You can skip some steps by
26
- caching values or looking at them and grabbing them manually.
27
29
  ```python
28
30
  import asyncio
29
31
 
30
- from roborock import HomeDataProduct, DeviceData, RoborockCommand
31
- from roborock.version_1_apis import RoborockMqttClientV1, RoborockLocalClientV1
32
32
  from roborock.web_api import RoborockApiClient
33
+ from roborock.devices.device_manager import create_device_manager, UserParams
34
+
33
35
 
34
36
  async def main():
35
37
  web_api = RoborockApiClient(username="youremailhere")
@@ -40,30 +42,31 @@ async def main():
40
42
  code = input("What is the code?")
41
43
  user_data = await web_api.code_login(code)
42
44
 
43
- # Get home data
44
- home_data = await web_api.get_home_data_v2(user_data)
45
-
46
- # Get the device you want
47
- device = home_data.devices[0]
48
-
49
- # Get product ids:
50
- product_info: dict[str, HomeDataProduct] = {
51
- product.id: product for product in home_data.products
52
- }
53
- # Create the Mqtt(aka cloud required) Client
54
- device_data = DeviceData(device, product_info[device.product_id].model)
55
- mqtt_client = RoborockMqttClientV1(user_data, device_data)
56
- networking = await mqtt_client.get_networking()
57
- local_device_data = DeviceData(device, product_info[device.product_id].model, networking.ip)
58
- local_client = RoborockLocalClientV1(local_device_data)
59
- # You can use the send_command to send any command to the device
60
- status = await local_client.send_command(RoborockCommand.GET_STATUS)
61
- # Or use existing functions that will give you data classes
62
- status = await local_client.get_status()
45
+ # Create a device manager that can discover devices.
46
+ user_params = UserParams(
47
+ username="youremailhere",
48
+ user_data=user_data,
49
+ )
50
+ device_manager = await create_device_manager(user_params)
51
+ devices = await device_manager.get_devices()
52
+
53
+ # Get all vacuum devices that support the v1 PropertiesApi
54
+ for device in devices:
55
+ if not device.v1_properties:
56
+ continue
57
+
58
+ # Refresh the current device status
59
+ status_trait = device.v1_properties.status
60
+ await status_trait.refresh()
61
+ print(status_trait)
63
62
 
64
63
  asyncio.run(main())
65
64
  ```
66
65
 
66
+ See [examples/example.py](examples/example.py) for a more full featured example
67
+ that has performance improvements to cache cloud information to prefer
68
+ connections over the local network.
69
+
67
70
  ## Supported devices
68
71
 
69
72
  You can find what devices are supported
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-roborock"
3
- version = "2.58.1"
3
+ version = "3.10.8"
4
4
  description = "A package to control Roborock vacuums."
5
5
  authors = [{ name = "humbertogontijo", email = "humbertogontijo@users.noreply.github.com" }, {name="Lash-L"}, {name="allenporter"}]
6
6
  requires-python = ">=3.11, <4"
@@ -44,7 +44,7 @@ dev = [
44
44
  "pytest",
45
45
  "pre-commit>=3.5,<5.0",
46
46
  "mypy",
47
- "ruff==0.14.0",
47
+ "ruff==0.14.6",
48
48
  "codespell",
49
49
  "pyshark>=0.6,<0.7",
50
50
  "aioresponses>=0.7.7,<0.8",
@@ -52,6 +52,9 @@ dev = [
52
52
  "pytest-timeout>=2.3.1,<3",
53
53
  "syrupy>=4.9.1,<5",
54
54
  "pdoc>=15.0.4,<16",
55
+ "pyyaml>=6.0.3",
56
+ "pyshark>=0.6",
57
+ "pytest-cov>=7.0.0",
55
58
  ]
56
59
 
57
60
  [tool.hatch.build.targets.sdist]
@@ -66,8 +69,19 @@ build-backend = "hatchling.build"
66
69
 
67
70
  [tool.semantic_release]
68
71
  branch = "main"
69
- version_toml = ["pyproject.toml:tool.poetry.version"]
70
- build_command = "pip install poetry && poetry build"
72
+ version_toml = ["pyproject.toml:project.version"]
73
+ build_command = "pip install uv && uv lock --upgrade-package python-roborock && git add uv.lock && uv build"
74
+ changelog_file = 'CHANGELOG.md'
75
+ commit = true
76
+
77
+ [tool.semantic_release.branches.main]
78
+ match = "main"
79
+ prerelease = false
80
+
81
+ [tool.semantic_release.branches.temp-main-branch]
82
+ match = "temp-main-branch"
83
+ prerelease = false
84
+
71
85
 
72
86
  [tool.semantic_release.commit_parser_options]
73
87
  allowed_tags = [
@@ -92,9 +106,3 @@ asyncio_mode = "auto"
92
106
  asyncio_default_fixture_loop_scope = "function"
93
107
  timeout = 30
94
108
  log_format = "%(asctime)s.%(msecs)03d %(levelname)s (%(threadName)s) [%(name)s] %(message)s"
95
-
96
- [tool.uv]
97
- dev-dependencies = [
98
- "pyyaml>=6.0.3",
99
- "pyshark>=0.6",
100
- ]
@@ -0,0 +1,30 @@
1
+ """Roborock API.
2
+
3
+ .. include:: ../README.md
4
+ """
5
+
6
+ from roborock.data import *
7
+ from roborock.exceptions import *
8
+ from roborock.roborock_typing import *
9
+
10
+ from . import (
11
+ cloud_api,
12
+ const,
13
+ data,
14
+ devices,
15
+ exceptions,
16
+ roborock_typing,
17
+ version_1_apis,
18
+ version_a01_apis,
19
+ web_api,
20
+ )
21
+
22
+ __all__ = [
23
+ "devices",
24
+ "data",
25
+ "map",
26
+ "web_api",
27
+ "roborock_typing",
28
+ "exceptions",
29
+ "const",
30
+ ]
@@ -8,7 +8,7 @@ import time
8
8
  from abc import ABC, abstractmethod
9
9
  from typing import Any
10
10
 
11
- from .containers import (
11
+ from .data import (
12
12
  DeviceData,
13
13
  )
14
14
  from .exceptions import (
@@ -19,7 +19,7 @@ from construct import ( # type: ignore
19
19
  from Crypto.Cipher import AES
20
20
 
21
21
  from roborock import RoborockException
22
- from roborock.containers import BroadcastMessage
22
+ from roborock.data import BroadcastMessage
23
23
  from roborock.protocol import EncryptionAdapter, Utils, _Parser
24
24
 
25
25
  _LOGGER = logging.getLogger(__name__)
@@ -42,11 +42,11 @@ from pyshark.capture.live_capture import LiveCapture, UnknownInterfaceException
42
42
  from pyshark.packet.packet import Packet # type: ignore
43
43
 
44
44
  from roborock import SHORT_MODEL_TO_ENUM, RoborockCommand
45
- from roborock.containers import CombinedMapInfo, DeviceData, HomeData, NetworkInfo, RoborockBase, UserData
45
+ from roborock.data import DeviceData, RoborockBase, UserData
46
46
  from roborock.device_features import DeviceFeatures
47
47
  from roborock.devices.cache import Cache, CacheData
48
48
  from roborock.devices.device import RoborockDevice
49
- from roborock.devices.device_manager import DeviceManager, create_device_manager, create_home_data_api
49
+ from roborock.devices.device_manager import DeviceManager, UserParams, create_device_manager
50
50
  from roborock.devices.traits import Trait
51
51
  from roborock.devices.traits.v1 import V1TraitMixin
52
52
  from roborock.devices.traits.v1.consumeable import ConsumableAttribute
@@ -116,9 +116,8 @@ class ConnectionCache(RoborockBase):
116
116
 
117
117
  user_data: UserData
118
118
  email: str
119
- home_data: HomeData | None = None
120
- network_info: dict[str, NetworkInfo] | None = None
121
- home_cache: dict[int, CombinedMapInfo] | None = None
119
+ # TODO: Used new APIs for cache file storage
120
+ cache_data: CacheData | None = None
122
121
 
123
122
 
124
123
  class DeviceConnectionManager:
@@ -133,9 +132,12 @@ class DeviceConnectionManager:
133
132
  async def ensure_device_manager(self) -> DeviceManager:
134
133
  """Ensure device manager is initialized."""
135
134
  if self.device_manager is None:
136
- cache_data = self.context.cache_data()
137
- home_data_api = create_home_data_api(cache_data.email, cache_data.user_data)
138
- self.device_manager = await create_device_manager(cache_data.user_data, home_data_api, self.context)
135
+ connection_cache = self.context.connection_cache()
136
+ user_params = UserParams(
137
+ username=connection_cache.email,
138
+ user_data=connection_cache.user_data,
139
+ )
140
+ self.device_manager = await create_device_manager(user_params, cache=self.context)
139
141
  # Cache devices for quick lookup
140
142
  devices = await self.device_manager.get_devices()
141
143
  self._devices = {device.duid: device for device in devices}
@@ -160,7 +162,8 @@ class RoborockContext(Cache):
160
162
  """Context that handles both CLI and session modes internally."""
161
163
 
162
164
  roborock_file = Path("~/.roborock").expanduser()
163
- _cache_data: ConnectionCache | None = None
165
+ roborock_cache_file = Path("~/.roborock.cache").expanduser()
166
+ _connection_cache: ConnectionCache | None = None
164
167
 
165
168
  def __init__(self):
166
169
  self.reload()
@@ -173,22 +176,22 @@ class RoborockContext(Cache):
173
176
  with open(self.roborock_file) as f:
174
177
  data = json.load(f)
175
178
  if data:
176
- self._cache_data = ConnectionCache.from_dict(data)
179
+ self._connection_cache = ConnectionCache.from_dict(data)
177
180
 
178
- def update(self, cache_data: ConnectionCache):
179
- data = json.dumps(cache_data.as_dict(), default=vars, indent=4)
181
+ def update(self, connection_cache: ConnectionCache):
182
+ data = json.dumps(connection_cache.as_dict(), default=vars, indent=4)
180
183
  with open(self.roborock_file, "w") as f:
181
184
  f.write(data)
182
185
  self.reload()
183
186
 
184
187
  def validate(self):
185
- if self._cache_data is None:
188
+ if self._connection_cache is None:
186
189
  raise RoborockException("You must login first")
187
190
 
188
- def cache_data(self) -> ConnectionCache:
191
+ def connection_cache(self) -> ConnectionCache:
189
192
  """Get the cache data."""
190
193
  self.validate()
191
- return cast(ConnectionCache, self._cache_data)
194
+ return cast(ConnectionCache, self._connection_cache)
192
195
 
193
196
  def start_session_mode(self):
194
197
  """Start session mode with a background event loop."""
@@ -225,19 +228,21 @@ class RoborockContext(Cache):
225
228
 
226
229
  async def refresh_devices(self) -> ConnectionCache:
227
230
  """Refresh device data from server (always fetches fresh data)."""
228
- cache_data = self.cache_data()
229
- client = RoborockApiClient(cache_data.email)
230
- home_data = await client.get_home_data_v3(cache_data.user_data)
231
- cache_data.home_data = home_data
232
- self.update(cache_data)
233
- return cache_data
231
+ connection_cache = self.connection_cache()
232
+ client = RoborockApiClient(connection_cache.email)
233
+ home_data = await client.get_home_data_v3(connection_cache.user_data)
234
+ if connection_cache.cache_data is None:
235
+ connection_cache.cache_data = CacheData()
236
+ connection_cache.cache_data.home_data = home_data
237
+ self.update(connection_cache)
238
+ return connection_cache
234
239
 
235
240
  async def get_devices(self) -> ConnectionCache:
236
241
  """Get device data (uses cache if available, fetches if needed)."""
237
- cache_data = self.cache_data()
238
- if not cache_data.home_data:
239
- cache_data = await self.refresh_devices()
240
- return cache_data
242
+ connection_cache = self.connection_cache()
243
+ if (connection_cache.cache_data is None) or (connection_cache.cache_data.home_data is None):
244
+ connection_cache = await self.refresh_devices()
245
+ return connection_cache
241
246
 
242
247
  async def cleanup(self):
243
248
  """Clean up resources (mainly for session mode)."""
@@ -262,20 +267,16 @@ class RoborockContext(Cache):
262
267
  async def get(self) -> CacheData:
263
268
  """Get cached value."""
264
269
  _LOGGER.debug("Getting cache data")
265
- connection_cache = self.cache_data()
266
- return CacheData(
267
- home_data=connection_cache.home_data,
268
- network_info=connection_cache.network_info or {},
269
- home_cache=connection_cache.home_cache,
270
- )
270
+ connection_cache = self.connection_cache()
271
+ if connection_cache.cache_data is not None:
272
+ return connection_cache.cache_data
273
+ return CacheData()
271
274
 
272
275
  async def set(self, value: CacheData) -> None:
273
276
  """Set value in the cache."""
274
277
  _LOGGER.debug("Setting cache data")
275
- connection_cache = self.cache_data()
276
- connection_cache.home_data = value.home_data
277
- connection_cache.network_info = value.network_info
278
- connection_cache.home_cache = value.home_cache
278
+ connection_cache = self.connection_cache()
279
+ connection_cache.cache_data = value
279
280
  self.update(connection_cache)
280
281
 
281
282
 
@@ -361,9 +362,9 @@ async def discover(ctx):
361
362
  @async_command
362
363
  async def list_devices(ctx):
363
364
  context: RoborockContext = ctx.obj
364
- cache_data = await context.get_devices()
365
+ connection_cache = await context.get_devices()
365
366
 
366
- home_data = cache_data.home_data
367
+ home_data = connection_cache.cache_data.home_data
367
368
 
368
369
  device_name_id = {device.name: device.duid for device in home_data.get_all_devices()}
369
370
  click.echo(json.dumps(device_name_id, indent=4))
@@ -401,9 +402,11 @@ async def _v1_trait(context: RoborockContext, device_id: str, display_func: Call
401
402
  device_manager = await context.get_device_manager()
402
403
  device = await device_manager.get_device(device_id)
403
404
  if device.v1_properties is None:
404
- raise RoborockException(f"Device {device.name} does not support V1 protocol")
405
+ raise RoborockUnsupportedFeature(f"Device {device.name} does not support V1 protocol")
405
406
  await device.v1_properties.discover_features()
406
407
  trait = display_func(device.v1_properties)
408
+ if trait is None:
409
+ raise RoborockUnsupportedFeature("Trait not supported by device")
407
410
  await trait.refresh()
408
411
  return trait
409
412
 
@@ -440,6 +443,26 @@ async def clean_summary(ctx, device_id: str):
440
443
  await _display_v1_trait(context, device_id, lambda v1: v1.clean_summary)
441
444
 
442
445
 
446
+ @session.command()
447
+ @click.option("--device_id", required=True)
448
+ @click.pass_context
449
+ @async_command
450
+ async def clean_record(ctx, device_id: str):
451
+ """Get device last clean record."""
452
+ context: RoborockContext = ctx.obj
453
+ await _display_v1_trait(context, device_id, lambda v1: v1.clean_record)
454
+
455
+
456
+ @session.command()
457
+ @click.option("--device_id", required=True)
458
+ @click.pass_context
459
+ @async_command
460
+ async def dock_summary(ctx, device_id: str):
461
+ """Get device dock summary."""
462
+ context: RoborockContext = ctx.obj
463
+ await _display_v1_trait(context, device_id, lambda v1: v1.dock_summary)
464
+
465
+
443
466
  @session.command()
444
467
  @click.option("--device_id", required=True)
445
468
  @click.pass_context
@@ -692,20 +715,30 @@ async def home(ctx, device_id: str, refresh: bool):
692
715
  await home_trait.refresh()
693
716
 
694
717
  # Display the discovered home cache
695
- if home_trait.home_cache:
718
+ if home_trait.home_map_info:
696
719
  cache_summary = {
697
720
  map_flag: {
698
721
  "name": map_data.name,
699
722
  "room_count": len(map_data.rooms),
700
723
  "rooms": [{"segment_id": room.segment_id, "name": room.name} for room in map_data.rooms],
701
724
  }
702
- for map_flag, map_data in home_trait.home_cache.items()
725
+ for map_flag, map_data in home_trait.home_map_info.items()
703
726
  }
704
727
  click.echo(dump_json(cache_summary))
705
728
  else:
706
729
  click.echo("No maps discovered")
707
730
 
708
731
 
732
+ @session.command()
733
+ @click.option("--device_id", required=True)
734
+ @click.pass_context
735
+ @async_command
736
+ async def network_info(ctx, device_id: str):
737
+ """Get device network information."""
738
+ context: RoborockContext = ctx.obj
739
+ await _display_v1_trait(context, device_id, lambda v1: v1.network_info)
740
+
741
+
709
742
  @click.command()
710
743
  @click.option("--device_id", required=True)
711
744
  @click.option("--cmd", required=True)
@@ -938,6 +971,8 @@ cli.add_command(session)
938
971
  cli.add_command(get_device_info)
939
972
  cli.add_command(update_docs)
940
973
  cli.add_command(clean_summary)
974
+ cli.add_command(clean_record)
975
+ cli.add_command(dock_summary)
941
976
  cli.add_command(volume)
942
977
  cli.add_command(set_volume)
943
978
  cli.add_command(maps)
@@ -952,6 +987,7 @@ cli.add_command(child_lock)
952
987
  cli.add_command(dnd)
953
988
  cli.add_command(flow_led_status)
954
989
  cli.add_command(led_status)
990
+ cli.add_command(network_info)
955
991
 
956
992
 
957
993
  def main():
@@ -14,7 +14,7 @@ from paho.mqtt.enums import MQTTErrorCode
14
14
  from paho.mqtt.reasoncodes import ReasonCode # type: ignore
15
15
 
16
16
  from .api import KEEPALIVE, RoborockClient
17
- from .containers import DeviceData, UserData
17
+ from .data import DeviceData, UserData
18
18
  from .exceptions import RoborockException, VacuumError
19
19
  from .protocol import (
20
20
  Decoder,
@@ -0,0 +1,9 @@
1
+ """This module is meant to hold dataclasses and codemappings for various devices and protocols."""
2
+
3
+ from .b01_q7 import *
4
+ from .b01_q10 import *
5
+ from .code_mappings import *
6
+ from .containers import *
7
+ from .dyad import *
8
+ from .v1 import *
9
+ from .zeo import *
@@ -0,0 +1,2 @@
1
+ from .b01_q10_code_mappings import *
2
+ from .b01_q10_containers import *