python-roborock 3.7.4__tar.gz → 3.8.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 (97) hide show
  1. {python_roborock-3.7.4 → python_roborock-3.8.0}/PKG-INFO +25 -26
  2. {python_roborock-3.7.4 → python_roborock-3.8.0}/README.md +24 -25
  3. {python_roborock-3.7.4 → python_roborock-3.8.0}/pyproject.toml +1 -1
  4. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/__init__.py +5 -6
  5. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/README.md +5 -3
  6. python_roborock-3.8.0/roborock/devices/__init__.py +11 -0
  7. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/device.py +47 -0
  8. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/device_manager.py +1 -1
  9. python_roborock-3.8.0/roborock/devices/file_cache.py +70 -0
  10. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/b01/__init__.py +1 -2
  11. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/__init__.py +20 -20
  12. python_roborock-3.8.0/roborock/protocols/__init__.py +3 -0
  13. python_roborock-3.7.4/roborock/devices/__init__.py +0 -7
  14. {python_roborock-3.7.4 → python_roborock-3.8.0}/.gitignore +0 -0
  15. {python_roborock-3.7.4 → python_roborock-3.8.0}/LICENSE +0 -0
  16. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/api.py +0 -0
  17. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/broadcast_protocol.py +0 -0
  18. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/callbacks.py +0 -0
  19. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/cli.py +0 -0
  20. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/cloud_api.py +0 -0
  21. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/command_cache.py +0 -0
  22. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/const.py +0 -0
  23. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/__init__.py +0 -0
  24. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q10/__init__.py +0 -0
  25. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q10/b01_q10_code_mappings.py +0 -0
  26. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q10/b01_q10_containers.py +0 -0
  27. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q7/__init__.py +0 -0
  28. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q7/b01_q7_code_mappings.py +0 -0
  29. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/b01_q7/b01_q7_containers.py +0 -0
  30. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/code_mappings.py +0 -0
  31. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/containers.py +0 -0
  32. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/dyad/__init__.py +0 -0
  33. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/dyad/dyad_code_mappings.py +0 -0
  34. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/dyad/dyad_containers.py +0 -0
  35. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/v1/__init__.py +0 -0
  36. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/v1/v1_clean_modes.py +0 -0
  37. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/v1/v1_code_mappings.py +0 -0
  38. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/v1/v1_containers.py +0 -0
  39. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/zeo/__init__.py +0 -0
  40. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/zeo/zeo_code_mappings.py +0 -0
  41. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/data/zeo/zeo_containers.py +0 -0
  42. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/device_features.py +0 -0
  43. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/a01_channel.py +0 -0
  44. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/b01_channel.py +0 -0
  45. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/cache.py +0 -0
  46. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/channel.py +0 -0
  47. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/local_channel.py +0 -0
  48. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/mqtt_channel.py +0 -0
  49. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/__init__.py +0 -0
  50. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/a01/__init__.py +0 -0
  51. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/traits_mixin.py +0 -0
  52. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/child_lock.py +0 -0
  53. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
  54. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/command.py +0 -0
  55. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/common.py +0 -0
  56. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/consumeable.py +0 -0
  57. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/device_features.py +0 -0
  58. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
  59. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/dust_collection_mode.py +0 -0
  60. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/flow_led_status.py +0 -0
  61. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/home.py +0 -0
  62. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/led_status.py +0 -0
  63. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/map_content.py +0 -0
  64. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/maps.py +0 -0
  65. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/network_info.py +0 -0
  66. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/rooms.py +0 -0
  67. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/routines.py +0 -0
  68. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/smart_wash_params.py +0 -0
  69. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/status.py +0 -0
  70. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/valley_electricity_timer.py +0 -0
  71. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/volume.py +0 -0
  72. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/traits/v1/wash_towel_mode.py +0 -0
  73. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/v1_channel.py +0 -0
  74. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/devices/v1_rpc_channel.py +0 -0
  75. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/exceptions.py +0 -0
  76. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/map/__init__.py +0 -0
  77. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/map/map_parser.py +0 -0
  78. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/mqtt/__init__.py +0 -0
  79. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/mqtt/roborock_session.py +0 -0
  80. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/mqtt/session.py +0 -0
  81. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/protocol.py +0 -0
  82. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/protocols/a01_protocol.py +0 -0
  83. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/protocols/b01_protocol.py +0 -0
  84. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/protocols/v1_protocol.py +0 -0
  85. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/py.typed +0 -0
  86. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/roborock_future.py +0 -0
  87. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/roborock_message.py +0 -0
  88. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/roborock_typing.py +0 -0
  89. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/util.py +0 -0
  90. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_1_apis/__init__.py +0 -0
  91. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
  92. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
  93. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
  94. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_a01_apis/__init__.py +0 -0
  95. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
  96. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
  97. {python_roborock-3.7.4 → python_roborock-3.8.0}/roborock/web_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-roborock
3
- Version: 3.7.4
3
+ Version: 3.8.0
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/
@@ -49,16 +49,14 @@ Install this via pip (or your favourite package manager):
49
49
 
50
50
  You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
51
51
 
52
- ## Sending Commands
52
+ ## Example Usage
53
53
 
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
54
  ```python
57
55
  import asyncio
58
56
 
59
- from roborock import HomeDataProduct, DeviceData, RoborockCommand
60
- from roborock.version_1_apis import RoborockMqttClientV1, RoborockLocalClientV1
61
57
  from roborock.web_api import RoborockApiClient
58
+ from roborock.devices.device_manager import create_device_manager, UserParams
59
+
62
60
 
63
61
  async def main():
64
62
  web_api = RoborockApiClient(username="youremailhere")
@@ -69,30 +67,31 @@ async def main():
69
67
  code = input("What is the code?")
70
68
  user_data = await web_api.code_login(code)
71
69
 
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()
70
+ # Create a device manager that can discover devices.
71
+ user_params = UserParams(
72
+ username="youremailhere",
73
+ user_data=user_data,
74
+ )
75
+ device_manager = await create_device_manager(user_params)
76
+ devices = await device_manager.get_devices()
77
+
78
+ # Get all vacuum devices that support the v1 PropertiesApi
79
+ for device in devices:
80
+ if not device.v1_properties:
81
+ continue
82
+
83
+ # Refresh the current device status
84
+ status_trait = device.v1_properties.status
85
+ await status_trait.refresh()
86
+ print(status_trait)
92
87
 
93
88
  asyncio.run(main())
94
89
  ```
95
90
 
91
+ See [examples/example.py](examples/example.py) for a more full featured example
92
+ that has performance improvements to cache cloud information to prefer
93
+ connections over the local network.
94
+
96
95
  ## Supported devices
97
96
 
98
97
  You can find what devices are supported
@@ -20,16 +20,14 @@ Install this via pip (or your favourite package manager):
20
20
 
21
21
  You can see all of the commands supported [here](https://python-roborock.readthedocs.io/en/latest/api_commands.html)
22
22
 
23
- ## Sending Commands
23
+ ## Example Usage
24
24
 
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
25
  ```python
28
26
  import asyncio
29
27
 
30
- from roborock import HomeDataProduct, DeviceData, RoborockCommand
31
- from roborock.version_1_apis import RoborockMqttClientV1, RoborockLocalClientV1
32
28
  from roborock.web_api import RoborockApiClient
29
+ from roborock.devices.device_manager import create_device_manager, UserParams
30
+
33
31
 
34
32
  async def main():
35
33
  web_api = RoborockApiClient(username="youremailhere")
@@ -40,30 +38,31 @@ async def main():
40
38
  code = input("What is the code?")
41
39
  user_data = await web_api.code_login(code)
42
40
 
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()
41
+ # Create a device manager that can discover devices.
42
+ user_params = UserParams(
43
+ username="youremailhere",
44
+ user_data=user_data,
45
+ )
46
+ device_manager = await create_device_manager(user_params)
47
+ devices = await device_manager.get_devices()
48
+
49
+ # Get all vacuum devices that support the v1 PropertiesApi
50
+ for device in devices:
51
+ if not device.v1_properties:
52
+ continue
53
+
54
+ # Refresh the current device status
55
+ status_trait = device.v1_properties.status
56
+ await status_trait.refresh()
57
+ print(status_trait)
63
58
 
64
59
  asyncio.run(main())
65
60
  ```
66
61
 
62
+ See [examples/example.py](examples/example.py) for a more full featured example
63
+ that has performance improvements to cache cloud information to prefer
64
+ connections over the local network.
65
+
67
66
  ## Supported devices
68
67
 
69
68
  You can find what devices are supported
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-roborock"
3
- version = "3.7.4"
3
+ version = "3.8.0"
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"
@@ -11,6 +11,7 @@ from . import (
11
11
  cloud_api,
12
12
  const,
13
13
  data,
14
+ devices,
14
15
  exceptions,
15
16
  roborock_typing,
16
17
  version_1_apis,
@@ -19,13 +20,11 @@ from . import (
19
20
  )
20
21
 
21
22
  __all__ = [
23
+ "devices",
24
+ "data",
25
+ "map",
22
26
  "web_api",
23
- "version_1_apis",
24
- "version_a01_apis",
25
- "const",
26
- "cloud_api",
27
27
  "roborock_typing",
28
28
  "exceptions",
29
- "data",
30
- # Add new APIs here in the future when they are public e.g. devices/
29
+ "const",
31
30
  ]
@@ -1,6 +1,8 @@
1
- # Roborock Device Discovery
1
+ # Roborock Devices & Discovery
2
2
 
3
- This page documents the full lifecycle of device discovery across Cloud and Network.
3
+ The devices module provides functionality to discover Roborock devices on the
4
+ network. This section documents the full lifecycle of device discovery across
5
+ Cloud and Network.
4
6
 
5
7
  ## Init account setup
6
8
 
@@ -61,7 +63,7 @@ that a newer version of the API should be used.
61
63
 
62
64
  ## Design
63
65
 
64
- ### Current API Issues
66
+ ### Prior API Issues
65
67
 
66
68
  - Complex Inheritance Hierarchy: Multiple inheritance with classes like RoborockMqttClientV1 inheriting from both RoborockMqttClient and RoborockClientV1
67
69
 
@@ -0,0 +1,11 @@
1
+ """
2
+ .. include:: ./README.md
3
+ """
4
+
5
+ __all__ = [
6
+ "device",
7
+ "device_manager",
8
+ "cache",
9
+ "file_cache",
10
+ "traits",
11
+ ]
@@ -4,12 +4,15 @@ This interface is experimental and subject to breaking changes without notice
4
4
  until the API is stable.
5
5
  """
6
6
 
7
+ import asyncio
8
+ import datetime
7
9
  import logging
8
10
  from abc import ABC
9
11
  from collections.abc import Callable, Mapping
10
12
  from typing import Any, TypeVar, cast
11
13
 
12
14
  from roborock.data import HomeDataDevice, HomeDataProduct
15
+ from roborock.exceptions import RoborockException
13
16
  from roborock.roborock_message import RoborockMessage
14
17
 
15
18
  from .channel import Channel
@@ -22,6 +25,11 @@ __all__ = [
22
25
  "RoborockDevice",
23
26
  ]
24
27
 
28
+ # Exponential backoff parameters
29
+ MIN_BACKOFF_INTERVAL = datetime.timedelta(seconds=10)
30
+ MAX_BACKOFF_INTERVAL = datetime.timedelta(minutes=30)
31
+ BACKOFF_MULTIPLIER = 1.5
32
+
25
33
 
26
34
  class RoborockDevice(ABC, TraitsMixin):
27
35
  """A generic channel for establishing a connection with a Roborock device.
@@ -54,6 +62,7 @@ class RoborockDevice(ABC, TraitsMixin):
54
62
  self._device_info = device_info
55
63
  self._product = product
56
64
  self._channel = channel
65
+ self._connect_task: asyncio.Task[None] | None = None
57
66
  self._unsub: Callable[[], None] | None = None
58
67
 
59
68
  @property
@@ -98,6 +107,38 @@ class RoborockDevice(ABC, TraitsMixin):
98
107
  """
99
108
  return self._channel.is_local_connected
100
109
 
110
+ def start_connect(self) -> None:
111
+ """Start a background task to connect to the device.
112
+
113
+ This will attempt to connect to the device using the appropriate protocol
114
+ channel. If the connection fails, it will retry with exponential backoff.
115
+
116
+ Once connected, the device will remain connected until `close()` is
117
+ called. The device will automatically attempt to reconnect if the connection
118
+ is lost.
119
+ """
120
+
121
+ async def connect_loop() -> None:
122
+ backoff = MIN_BACKOFF_INTERVAL
123
+ try:
124
+ while True:
125
+ try:
126
+ await self.connect()
127
+ return
128
+ except RoborockException as e:
129
+ _LOGGER.info("Failed to connect to device %s: %s", self.name, e)
130
+ _LOGGER.info(
131
+ "Retrying connection to device %s in %s seconds", self.name, backoff.total_seconds()
132
+ )
133
+ await asyncio.sleep(backoff.total_seconds())
134
+ backoff = min(backoff * BACKOFF_MULTIPLIER, MAX_BACKOFF_INTERVAL)
135
+ except asyncio.CancelledError:
136
+ _LOGGER.info("connect_loop for device %s was cancelled", self.name)
137
+ # Clean exit on cancellation
138
+ return
139
+
140
+ self._connect_task = asyncio.create_task(connect_loop())
141
+
101
142
  async def connect(self) -> None:
102
143
  """Connect to the device using the appropriate protocol channel."""
103
144
  if self._unsub:
@@ -107,6 +148,12 @@ class RoborockDevice(ABC, TraitsMixin):
107
148
 
108
149
  async def close(self) -> None:
109
150
  """Close all connections to the device."""
151
+ if self._connect_task:
152
+ self._connect_task.cancel()
153
+ try:
154
+ await self._connect_task
155
+ except asyncio.CancelledError:
156
+ pass
110
157
  if self._unsub:
111
158
  self._unsub()
112
159
  self._unsub = None
@@ -86,7 +86,7 @@ class DeviceManager:
86
86
  if duid in self._devices:
87
87
  continue
88
88
  new_device = self._device_creator(home_data, device, product)
89
- await new_device.connect()
89
+ new_device.start_connect()
90
90
  new_devices[duid] = new_device
91
91
 
92
92
  self._devices.update(new_devices)
@@ -0,0 +1,70 @@
1
+ """This module implements a file-backed cache for device information.
2
+
3
+ This module provides a `FileCache` class that implements the `Cache` protocol
4
+ to store and retrieve cached device information from a file on disk. This allows
5
+ persistent caching of device data across application restarts.
6
+ """
7
+
8
+ import asyncio
9
+ import pathlib
10
+ import pickle
11
+ from collections.abc import Callable
12
+ from typing import Any
13
+
14
+ from .cache import Cache, CacheData
15
+
16
+
17
+ class FileCache(Cache):
18
+ """File backed cache implementation."""
19
+
20
+ def __init__(self, file_path: pathlib.Path, init_fn: Callable[[], CacheData] = CacheData) -> None:
21
+ """Initialize the file cache with the given file path."""
22
+ self._init_fn = init_fn
23
+ self._file_path = file_path
24
+ self._cache_data: CacheData | None = None
25
+
26
+ async def get(self) -> CacheData:
27
+ """Get cached value."""
28
+ if self._cache_data is not None:
29
+ return self._cache_data
30
+
31
+ data = await load_value(self._file_path)
32
+ if data is not None and not isinstance(data, CacheData):
33
+ raise TypeError(f"Invalid cache data loaded from {self._file_path}")
34
+
35
+ self._cache_data = data or self._init_fn()
36
+ return self._cache_data
37
+
38
+ async def set(self, value: CacheData) -> None: # type: ignore[override]
39
+ """Set value in the cache."""
40
+ self._cache_data = value
41
+
42
+ async def flush(self) -> None:
43
+ """Flush the cache to disk."""
44
+ if self._cache_data is None:
45
+ return
46
+ await store_value(self._file_path, self._cache_data)
47
+
48
+
49
+ async def store_value(file_path: pathlib.Path, value: Any) -> None:
50
+ """Store a value to the given file path."""
51
+
52
+ def _store_to_disk(file_path: pathlib.Path, value: Any) -> None:
53
+ with open(file_path, "wb") as f:
54
+ data = pickle.dumps(value)
55
+ f.write(data)
56
+
57
+ await asyncio.to_thread(_store_to_disk, file_path, value)
58
+
59
+
60
+ async def load_value(file_path: pathlib.Path) -> Any | None:
61
+ """Load a value from the given file path."""
62
+
63
+ def _load_from_disk(file_path: pathlib.Path) -> Any | None:
64
+ if not file_path.exists():
65
+ return None
66
+ with open(file_path, "rb") as f:
67
+ data = f.read()
68
+ return pickle.loads(data)
69
+
70
+ return await asyncio.to_thread(_load_from_disk, file_path)
@@ -6,8 +6,7 @@ from roborock.devices.mqtt_channel import MqttChannel
6
6
  from roborock.devices.traits import Trait
7
7
  from roborock.roborock_message import RoborockB01Props
8
8
 
9
- __init__ = [
10
- "create_b01_traits",
9
+ __all__ = [
11
10
  "PropertiesApi",
12
11
  ]
13
12
 
@@ -67,27 +67,27 @@ from .wash_towel_mode import WashTowelModeTrait
67
67
  _LOGGER = logging.getLogger(__name__)
68
68
 
69
69
  __all__ = [
70
- "create",
71
70
  "PropertiesApi",
72
- "StatusTrait",
73
- "DoNotDisturbTrait",
74
- "CleanSummaryTrait",
75
- "SoundVolumeTrait",
76
- "MapsTrait",
77
- "MapContentTrait",
78
- "ConsumableTrait",
79
- "HomeTrait",
80
- "DeviceFeaturesTrait",
81
- "CommandTrait",
82
- "ChildLockTrait",
83
- "FlowLedStatusTrait",
84
- "LedStatusTrait",
85
- "ValleyElectricityTimerTrait",
86
- "DustCollectionModeTrait",
87
- "WashTowelModeTrait",
88
- "SmartWashParamsTrait",
89
- "NetworkInfoTrait",
90
- "RoutinesTrait",
71
+ "child_lock",
72
+ "clean_summary",
73
+ "common",
74
+ "consumeable",
75
+ "device_features",
76
+ "do_not_disturb",
77
+ "dust_collection_mode",
78
+ "flow_led_status",
79
+ "home",
80
+ "led_status",
81
+ "map_content",
82
+ "maps",
83
+ "network_info",
84
+ "rooms",
85
+ "routines",
86
+ "smart_wash_params",
87
+ "status",
88
+ "valley_electricity_timer",
89
+ "volume",
90
+ "wash_towel_mode",
91
91
  ]
92
92
 
93
93
 
@@ -0,0 +1,3 @@
1
+ """Protocols for communicating with Roborock devices."""
2
+
3
+ __all__: list[str] = []
@@ -1,7 +0,0 @@
1
- """The devices module provides functionality to discover Roborock devices on the network."""
2
-
3
- __all__ = [
4
- "device",
5
- "device_manager",
6
- "cache",
7
- ]
File without changes