python-roborock 2.47.0__tar.gz → 2.48.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.
- {python_roborock-2.47.0 → python_roborock-2.48.0}/PKG-INFO +1 -1
- {python_roborock-2.47.0 → python_roborock-2.48.0}/pyproject.toml +2 -1
- python_roborock-2.48.0/roborock/__init__.py +39 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/device_manager.py +2 -2
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/v1_channel.py +109 -24
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/mqtt/roborock_session.py +60 -0
- python_roborock-2.47.0/roborock/__init__.py +0 -7
- {python_roborock-2.47.0 → python_roborock-2.48.0}/LICENSE +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/README.md +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/api.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/b01_containers.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/callbacks.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/clean_modes.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/cli.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/cloud_api.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/code_mappings.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/command_cache.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/const.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/containers.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/device_features.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/README.md +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/b01_channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/cache.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/device.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/local_channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/a01/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/traits_mixin.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/clean_summary.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/common.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/do_not_disturb.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/status.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/volume.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/v1_rpc_channel.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/exceptions.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/mqtt/session.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/protocol.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/protocols/b01_protocol.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/protocols/v1_protocol.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/py.typed +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/roborock_future.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/roborock_message.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/util.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/web_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-roborock"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.48.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"
|
|
@@ -51,6 +51,7 @@ aioresponses = "^0.7.7"
|
|
|
51
51
|
freezegun = "^1.5.1"
|
|
52
52
|
pytest-timeout = "^2.3.1"
|
|
53
53
|
syrupy = "^4.9.1"
|
|
54
|
+
pdoc = "^15.0.4"
|
|
54
55
|
|
|
55
56
|
[tool.semantic_release]
|
|
56
57
|
branch = "main"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Roborock API.
|
|
2
|
+
|
|
3
|
+
.. include:: ../README.md
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from roborock.b01_containers import *
|
|
7
|
+
from roborock.code_mappings import *
|
|
8
|
+
from roborock.containers import *
|
|
9
|
+
from roborock.exceptions import *
|
|
10
|
+
from roborock.roborock_typing import *
|
|
11
|
+
|
|
12
|
+
from . import (
|
|
13
|
+
b01_containers,
|
|
14
|
+
clean_modes,
|
|
15
|
+
cloud_api,
|
|
16
|
+
code_mappings,
|
|
17
|
+
const,
|
|
18
|
+
containers,
|
|
19
|
+
exceptions,
|
|
20
|
+
roborock_typing,
|
|
21
|
+
version_1_apis,
|
|
22
|
+
version_a01_apis,
|
|
23
|
+
web_api,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"web_api",
|
|
28
|
+
"version_1_apis",
|
|
29
|
+
"version_a01_apis",
|
|
30
|
+
"containers",
|
|
31
|
+
"b01_containers",
|
|
32
|
+
"const",
|
|
33
|
+
"cloud_api",
|
|
34
|
+
"clean_modes",
|
|
35
|
+
"code_mappings",
|
|
36
|
+
"roborock_typing",
|
|
37
|
+
"exceptions",
|
|
38
|
+
# Add new APIs here in the future when they are public e.g. devices/
|
|
39
|
+
]
|
|
@@ -14,7 +14,7 @@ from roborock.containers import (
|
|
|
14
14
|
UserData,
|
|
15
15
|
)
|
|
16
16
|
from roborock.devices.device import RoborockDevice
|
|
17
|
-
from roborock.mqtt.roborock_session import
|
|
17
|
+
from roborock.mqtt.roborock_session import create_lazy_mqtt_session
|
|
18
18
|
from roborock.mqtt.session import MqttSession
|
|
19
19
|
from roborock.protocol import create_mqtt_params
|
|
20
20
|
from roborock.web_api import RoborockApiClient
|
|
@@ -141,7 +141,7 @@ async def create_device_manager(
|
|
|
141
141
|
cache = NoCache()
|
|
142
142
|
|
|
143
143
|
mqtt_params = create_mqtt_params(user_data.rriot)
|
|
144
|
-
mqtt_session = await
|
|
144
|
+
mqtt_session = await create_lazy_mqtt_session(mqtt_params)
|
|
145
145
|
|
|
146
146
|
def device_creator(device: HomeDataDevice, product: HomeDataProduct) -> RoborockDevice:
|
|
147
147
|
channel: Channel
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
This module provides a unified channel interface for V1 protocol devices,
|
|
4
4
|
handling both MQTT and local connections with automatic fallback.
|
|
5
5
|
"""
|
|
6
|
-
|
|
6
|
+
import asyncio
|
|
7
|
+
import datetime
|
|
7
8
|
import logging
|
|
8
9
|
from collections.abc import Callable
|
|
9
10
|
from typing import TypeVar
|
|
@@ -22,7 +23,12 @@ from .cache import Cache
|
|
|
22
23
|
from .channel import Channel
|
|
23
24
|
from .local_channel import LocalChannel, LocalSession, create_local_session
|
|
24
25
|
from .mqtt_channel import MqttChannel
|
|
25
|
-
from .v1_rpc_channel import
|
|
26
|
+
from .v1_rpc_channel import (
|
|
27
|
+
PickFirstAvailable,
|
|
28
|
+
V1RpcChannel,
|
|
29
|
+
create_local_rpc_channel,
|
|
30
|
+
create_mqtt_rpc_channel,
|
|
31
|
+
)
|
|
26
32
|
|
|
27
33
|
_LOGGER = logging.getLogger(__name__)
|
|
28
34
|
|
|
@@ -32,6 +38,15 @@ __all__ = [
|
|
|
32
38
|
|
|
33
39
|
_T = TypeVar("_T", bound=RoborockBase)
|
|
34
40
|
|
|
41
|
+
# Exponential backoff parameters for reconnecting to local
|
|
42
|
+
MIN_RECONNECT_INTERVAL = datetime.timedelta(minutes=1)
|
|
43
|
+
MAX_RECONNECT_INTERVAL = datetime.timedelta(minutes=10)
|
|
44
|
+
RECONNECT_MULTIPLIER = 1.5
|
|
45
|
+
# After this many hours, the network info is refreshed
|
|
46
|
+
NETWORK_INFO_REFRESH_INTERVAL = datetime.timedelta(hours=12)
|
|
47
|
+
# Interval to check that the local connection is healthy
|
|
48
|
+
LOCAL_CONNECTION_CHECK_INTERVAL = datetime.timedelta(seconds=15)
|
|
49
|
+
|
|
35
50
|
|
|
36
51
|
class V1Channel(Channel):
|
|
37
52
|
"""Unified V1 protocol channel with automatic MQTT/local connection handling.
|
|
@@ -69,6 +84,8 @@ class V1Channel(Channel):
|
|
|
69
84
|
self._local_unsub: Callable[[], None] | None = None
|
|
70
85
|
self._callback: Callable[[RoborockMessage], None] | None = None
|
|
71
86
|
self._cache = cache
|
|
87
|
+
self._reconnect_task: asyncio.Task[None] | None = None
|
|
88
|
+
self._last_network_info_refresh: datetime.datetime | None = None
|
|
72
89
|
|
|
73
90
|
@property
|
|
74
91
|
def is_connected(self) -> bool:
|
|
@@ -78,7 +95,7 @@ class V1Channel(Channel):
|
|
|
78
95
|
@property
|
|
79
96
|
def is_local_connected(self) -> bool:
|
|
80
97
|
"""Return whether local connection is available."""
|
|
81
|
-
return self.
|
|
98
|
+
return self._local_channel is not None and self._local_channel.is_connected
|
|
82
99
|
|
|
83
100
|
@property
|
|
84
101
|
def is_mqtt_connected(self) -> bool:
|
|
@@ -103,25 +120,35 @@ class V1Channel(Channel):
|
|
|
103
120
|
a RoborockException. A local connection failure will not raise an exception,
|
|
104
121
|
since the local connection is optional.
|
|
105
122
|
"""
|
|
123
|
+
if self._callback is not None:
|
|
124
|
+
raise ValueError("Only one subscription allowed at a time")
|
|
106
125
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
self._callback = callback
|
|
110
|
-
|
|
111
|
-
# First establish MQTT connection
|
|
112
|
-
self._mqtt_unsub = await self._mqtt_channel.subscribe(self._on_mqtt_message)
|
|
113
|
-
_LOGGER.debug("V1Channel connected to device %s via MQTT", self._device_uid)
|
|
114
|
-
|
|
115
|
-
# Try to establish an optional local connection as well.
|
|
126
|
+
# Make an initial, optimistic attempt to connect to local with the
|
|
127
|
+
# cache. The cache information will be refreshed by the background task.
|
|
116
128
|
try:
|
|
117
|
-
|
|
129
|
+
await self._local_connect(use_cache=True)
|
|
118
130
|
except RoborockException as err:
|
|
119
131
|
_LOGGER.warning("Could not establish local connection for device %s: %s", self._device_uid, err)
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
|
|
133
|
+
# Start a background task to manage the local connection health. This
|
|
134
|
+
# happens independent of whether we were able to connect locally now.
|
|
135
|
+
_LOGGER.info("self._reconnect_task=%s", self._reconnect_task)
|
|
136
|
+
if self._reconnect_task is None:
|
|
137
|
+
loop = asyncio.get_running_loop()
|
|
138
|
+
self._reconnect_task = loop.create_task(self._background_reconnect())
|
|
139
|
+
|
|
140
|
+
if not self.is_local_connected:
|
|
141
|
+
# We were not able to connect locally, so fallback to MQTT and at least
|
|
142
|
+
# establish that connection explicitly. If this fails then raise an
|
|
143
|
+
# error and let the caller know we failed to subscribe.
|
|
144
|
+
self._mqtt_unsub = await self._mqtt_channel.subscribe(self._on_mqtt_message)
|
|
145
|
+
_LOGGER.debug("V1Channel connected to device %s via MQTT", self._device_uid)
|
|
122
146
|
|
|
123
147
|
def unsub() -> None:
|
|
124
148
|
"""Unsubscribe from all messages."""
|
|
149
|
+
if self._reconnect_task:
|
|
150
|
+
self._reconnect_task.cancel()
|
|
151
|
+
self._reconnect_task = None
|
|
125
152
|
if self._mqtt_unsub:
|
|
126
153
|
self._mqtt_unsub()
|
|
127
154
|
self._mqtt_unsub = None
|
|
@@ -130,15 +157,16 @@ class V1Channel(Channel):
|
|
|
130
157
|
self._local_unsub = None
|
|
131
158
|
_LOGGER.debug("Unsubscribed from device %s", self._device_uid)
|
|
132
159
|
|
|
160
|
+
self._callback = callback
|
|
133
161
|
return unsub
|
|
134
162
|
|
|
135
|
-
async def _get_networking_info(self) -> NetworkInfo:
|
|
163
|
+
async def _get_networking_info(self, *, use_cache: bool = True) -> NetworkInfo:
|
|
136
164
|
"""Retrieve networking information for the device.
|
|
137
165
|
|
|
138
166
|
This is a cloud only command used to get the local device's IP address.
|
|
139
167
|
"""
|
|
140
168
|
cache_data = await self._cache.get()
|
|
141
|
-
if cache_data.network_info and (network_info := cache_data.network_info.get(self._device_uid)):
|
|
169
|
+
if use_cache and cache_data.network_info and (network_info := cache_data.network_info.get(self._device_uid)):
|
|
142
170
|
_LOGGER.debug("Using cached network info for device %s", self._device_uid)
|
|
143
171
|
return network_info
|
|
144
172
|
try:
|
|
@@ -148,24 +176,81 @@ class V1Channel(Channel):
|
|
|
148
176
|
except RoborockException as e:
|
|
149
177
|
raise RoborockException(f"Network info failed for device {self._device_uid}") from e
|
|
150
178
|
_LOGGER.debug("Network info for device %s: %s", self._device_uid, network_info)
|
|
179
|
+
self._last_network_info_refresh = datetime.datetime.now(datetime.timezone.utc)
|
|
151
180
|
cache_data.network_info[self._device_uid] = network_info
|
|
152
181
|
await self._cache.set(cache_data)
|
|
153
182
|
return network_info
|
|
154
183
|
|
|
155
|
-
async def _local_connect(self) ->
|
|
184
|
+
async def _local_connect(self, *, use_cache: bool = True) -> None:
|
|
156
185
|
"""Set up local connection if possible."""
|
|
157
|
-
_LOGGER.debug(
|
|
158
|
-
|
|
186
|
+
_LOGGER.debug(
|
|
187
|
+
"Attempting to connect to local channel for device %s (use_cache=%s)", self._device_uid, use_cache
|
|
188
|
+
)
|
|
189
|
+
networking_info = await self._get_networking_info(use_cache=use_cache)
|
|
159
190
|
host = networking_info.ip
|
|
160
191
|
_LOGGER.debug("Connecting to local channel at %s", host)
|
|
161
|
-
|
|
192
|
+
# Create a new local channel and connect
|
|
193
|
+
local_channel = self._local_session(host)
|
|
162
194
|
try:
|
|
163
|
-
await
|
|
195
|
+
await local_channel.connect()
|
|
164
196
|
except RoborockException as e:
|
|
165
|
-
self._local_channel = None
|
|
166
197
|
raise RoborockException(f"Error connecting to local device {self._device_uid}: {e}") from e
|
|
198
|
+
# Wire up the new channel
|
|
199
|
+
self._local_channel = local_channel
|
|
167
200
|
self._local_rpc_channel = create_local_rpc_channel(self._local_channel)
|
|
168
|
-
|
|
201
|
+
self._local_unsub = await self._local_channel.subscribe(self._on_local_message)
|
|
202
|
+
_LOGGER.info("Successfully connected to local device %s", self._device_uid)
|
|
203
|
+
|
|
204
|
+
async def _background_reconnect(self) -> None:
|
|
205
|
+
"""Task to run in the background to manage the local connection."""
|
|
206
|
+
_LOGGER.debug("Starting background task to manage local connection for %s", self._device_uid)
|
|
207
|
+
reconnect_backoff = MIN_RECONNECT_INTERVAL
|
|
208
|
+
local_connect_failures = 0
|
|
209
|
+
|
|
210
|
+
while True:
|
|
211
|
+
try:
|
|
212
|
+
if self.is_local_connected:
|
|
213
|
+
await asyncio.sleep(LOCAL_CONNECTION_CHECK_INTERVAL.total_seconds())
|
|
214
|
+
continue
|
|
215
|
+
|
|
216
|
+
# Not connected, so wait with backoff before trying to connect.
|
|
217
|
+
# The first time through, we don't sleep, we just try to connect.
|
|
218
|
+
local_connect_failures += 1
|
|
219
|
+
if local_connect_failures > 1:
|
|
220
|
+
await asyncio.sleep(reconnect_backoff.total_seconds())
|
|
221
|
+
reconnect_backoff = min(reconnect_backoff * RECONNECT_MULTIPLIER, MAX_RECONNECT_INTERVAL)
|
|
222
|
+
|
|
223
|
+
use_cache = self._should_use_cache(local_connect_failures)
|
|
224
|
+
await self._local_connect(use_cache=use_cache)
|
|
225
|
+
# Reset backoff and failures on success
|
|
226
|
+
reconnect_backoff = MIN_RECONNECT_INTERVAL
|
|
227
|
+
local_connect_failures = 0
|
|
228
|
+
|
|
229
|
+
except asyncio.CancelledError:
|
|
230
|
+
_LOGGER.debug("Background reconnect task cancelled")
|
|
231
|
+
if self._local_channel:
|
|
232
|
+
self._local_channel.close()
|
|
233
|
+
return
|
|
234
|
+
except RoborockException as err:
|
|
235
|
+
_LOGGER.debug("Background reconnect failed: %s", err)
|
|
236
|
+
except Exception:
|
|
237
|
+
_LOGGER.exception("Unhandled exception in background reconnect task")
|
|
238
|
+
|
|
239
|
+
def _should_use_cache(self, local_connect_failures: int) -> bool:
|
|
240
|
+
"""Determine whether to use cached network info on retries.
|
|
241
|
+
|
|
242
|
+
On the first retry we'll avoid the cache to handle the case where
|
|
243
|
+
the network ip may have recently changed. Otherwise, use the cache
|
|
244
|
+
if available then expire at some point.
|
|
245
|
+
"""
|
|
246
|
+
if local_connect_failures == 1:
|
|
247
|
+
return False
|
|
248
|
+
elif self._last_network_info_refresh and (
|
|
249
|
+
datetime.datetime.now(datetime.timezone.utc) - self._last_network_info_refresh
|
|
250
|
+
> NETWORK_INFO_REFRESH_INTERVAL
|
|
251
|
+
):
|
|
252
|
+
return False
|
|
253
|
+
return True
|
|
169
254
|
|
|
170
255
|
def _on_mqtt_message(self, message: RoborockMessage) -> None:
|
|
171
256
|
"""Handle incoming MQTT messages."""
|
|
@@ -220,6 +220,57 @@ class RoborockMqttSession(MqttSession):
|
|
|
220
220
|
raise MqttSessionException(f"Error publishing message: {err}") from err
|
|
221
221
|
|
|
222
222
|
|
|
223
|
+
class LazyMqttSession(MqttSession):
|
|
224
|
+
"""An MQTT session that is started on first attempt to subscribe.
|
|
225
|
+
|
|
226
|
+
This is a wrapper around an existing MqttSession that will only start
|
|
227
|
+
the underlying session when the first attempt to subscribe or publish
|
|
228
|
+
is made.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
def __init__(self, session: RoborockMqttSession) -> None:
|
|
232
|
+
"""Initialize the lazy session with an existing session."""
|
|
233
|
+
self._lock = asyncio.Lock()
|
|
234
|
+
self._started = False
|
|
235
|
+
self._session = session
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def connected(self) -> bool:
|
|
239
|
+
"""True if the session is connected to the broker."""
|
|
240
|
+
return self._session.connected
|
|
241
|
+
|
|
242
|
+
async def _maybe_start(self) -> None:
|
|
243
|
+
"""Start the MQTT session if not already started."""
|
|
244
|
+
async with self._lock:
|
|
245
|
+
if not self._started:
|
|
246
|
+
await self._session.start()
|
|
247
|
+
self._started = True
|
|
248
|
+
|
|
249
|
+
async def subscribe(self, device_id: str, callback: Callable[[bytes], None]) -> Callable[[], None]:
|
|
250
|
+
"""Invoke the callback when messages are received on the topic.
|
|
251
|
+
|
|
252
|
+
The returned callable unsubscribes from the topic when called.
|
|
253
|
+
"""
|
|
254
|
+
await self._maybe_start()
|
|
255
|
+
return await self._session.subscribe(device_id, callback)
|
|
256
|
+
|
|
257
|
+
async def publish(self, topic: str, message: bytes) -> None:
|
|
258
|
+
"""Publish a message on the specified topic.
|
|
259
|
+
|
|
260
|
+
This will raise an exception if the message could not be sent.
|
|
261
|
+
"""
|
|
262
|
+
await self._maybe_start()
|
|
263
|
+
return await self._session.publish(topic, message)
|
|
264
|
+
|
|
265
|
+
async def close(self) -> None:
|
|
266
|
+
"""Cancels the mqtt loop.
|
|
267
|
+
|
|
268
|
+
This will close the underlying session and will not allow it to be
|
|
269
|
+
restarted again.
|
|
270
|
+
"""
|
|
271
|
+
await self._session.close()
|
|
272
|
+
|
|
273
|
+
|
|
223
274
|
async def create_mqtt_session(params: MqttParams) -> MqttSession:
|
|
224
275
|
"""Create an MQTT session.
|
|
225
276
|
|
|
@@ -230,3 +281,12 @@ async def create_mqtt_session(params: MqttParams) -> MqttSession:
|
|
|
230
281
|
session = RoborockMqttSession(params)
|
|
231
282
|
await session.start()
|
|
232
283
|
return session
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async def create_lazy_mqtt_session(params: MqttParams) -> MqttSession:
|
|
287
|
+
"""Create a lazy MQTT session.
|
|
288
|
+
|
|
289
|
+
This function is a factory for creating an MQTT session that will
|
|
290
|
+
only connect when the first attempt to subscribe or publish is made.
|
|
291
|
+
"""
|
|
292
|
+
return LazyMqttSession(RoborockMqttSession(params))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/clean_summary.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/devices/traits/v1/do_not_disturb.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.47.0 → python_roborock-2.48.0}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|