python-roborock 2.43.0__tar.gz → 2.44.1__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.43.0 → python_roborock-2.44.1}/PKG-INFO +1 -1
- {python_roborock-2.43.0 → python_roborock-2.44.1}/pyproject.toml +1 -1
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/cli.py +43 -1
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/device_manager.py +8 -2
- python_roborock-2.44.1/roborock/devices/traits/sound_volume.py +31 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/v1_rpc_channel.py +4 -1
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/protocols/v1_protocol.py +28 -12
- {python_roborock-2.43.0 → python_roborock-2.44.1}/LICENSE +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/README.md +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/api.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/b01_containers.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/broadcast_protocol.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/callbacks.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/clean_modes.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/cloud_api.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/code_mappings.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/command_cache.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/const.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/containers.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/device_features.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/README.md +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/a01_channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/b01_channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/cache.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/device.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/local_channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/mqtt_channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/b01/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/b01/props.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/clean_summary.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/dnd.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/dyad.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/status.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/trait.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/traits/zeo.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/devices/v1_channel.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/exceptions.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/mqtt/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/mqtt/roborock_session.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/mqtt/session.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/protocol.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/protocols/a01_protocol.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/protocols/b01_protocol.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/py.typed +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/roborock_future.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/roborock_message.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/roborock_typing.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/util.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/roborock_client_v1.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/roborock_local_client_v1.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_a01_apis/__init__.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_a01_apis/roborock_client_a01.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_a01_apis/roborock_mqtt_client_a01.py +0 -0
- {python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/web_api.py +0 -0
|
@@ -382,7 +382,7 @@ async def execute_scene(ctx, scene_id):
|
|
|
382
382
|
@click.pass_context
|
|
383
383
|
@async_command
|
|
384
384
|
async def status(ctx, device_id: str):
|
|
385
|
-
"""Get device status
|
|
385
|
+
"""Get device status."""
|
|
386
386
|
context: RoborockContext = ctx.obj
|
|
387
387
|
|
|
388
388
|
device_manager = await context.get_device_manager()
|
|
@@ -414,6 +414,46 @@ async def clean_summary(ctx, device_id: str):
|
|
|
414
414
|
click.echo(dump_json(clean_summary_result.as_dict()))
|
|
415
415
|
|
|
416
416
|
|
|
417
|
+
@session.command()
|
|
418
|
+
@click.option("--device_id", required=True)
|
|
419
|
+
@click.pass_context
|
|
420
|
+
@async_command
|
|
421
|
+
async def volume(ctx, device_id: str):
|
|
422
|
+
"""Get device volume."""
|
|
423
|
+
context: RoborockContext = ctx.obj
|
|
424
|
+
|
|
425
|
+
device_manager = await context.get_device_manager()
|
|
426
|
+
device = await device_manager.get_device(device_id)
|
|
427
|
+
|
|
428
|
+
if not (volume_trait := device.traits.get("sound_volume")):
|
|
429
|
+
click.echo(f"Device {device.name} does not have a volume trait")
|
|
430
|
+
return
|
|
431
|
+
|
|
432
|
+
volume_result = await volume_trait.get_volume()
|
|
433
|
+
click.echo(f"Device {device_id} volume:")
|
|
434
|
+
click.echo(volume_result)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@session.command()
|
|
438
|
+
@click.option("--device_id", required=True)
|
|
439
|
+
@click.option("--volume", required=True, type=int)
|
|
440
|
+
@click.pass_context
|
|
441
|
+
@async_command
|
|
442
|
+
async def set_volume(ctx, device_id: str, volume: int):
|
|
443
|
+
"""Set the devicevolume."""
|
|
444
|
+
context: RoborockContext = ctx.obj
|
|
445
|
+
|
|
446
|
+
device_manager = await context.get_device_manager()
|
|
447
|
+
device = await device_manager.get_device(device_id)
|
|
448
|
+
|
|
449
|
+
if not (volume_trait := device.traits.get("sound_volume")):
|
|
450
|
+
click.echo(f"Device {device.name} does not have a volume trait")
|
|
451
|
+
return
|
|
452
|
+
|
|
453
|
+
await volume_trait.set_volume(volume)
|
|
454
|
+
click.echo(f"Set Device {device_id} volume to {volume}")
|
|
455
|
+
|
|
456
|
+
|
|
417
457
|
@click.command()
|
|
418
458
|
@click.option("--device_id", required=True)
|
|
419
459
|
@click.option("--cmd", required=True)
|
|
@@ -653,6 +693,8 @@ cli.add_command(session)
|
|
|
653
693
|
cli.add_command(get_device_info)
|
|
654
694
|
cli.add_command(update_docs)
|
|
655
695
|
cli.add_command(clean_summary)
|
|
696
|
+
cli.add_command(volume)
|
|
697
|
+
cli.add_command(set_volume)
|
|
656
698
|
|
|
657
699
|
|
|
658
700
|
def main():
|
|
@@ -5,6 +5,8 @@ import enum
|
|
|
5
5
|
import logging
|
|
6
6
|
from collections.abc import Awaitable, Callable
|
|
7
7
|
|
|
8
|
+
import aiohttp
|
|
9
|
+
|
|
8
10
|
from roborock.code_mappings import RoborockCategory
|
|
9
11
|
from roborock.containers import (
|
|
10
12
|
HomeData,
|
|
@@ -25,6 +27,7 @@ from .traits.b01.props import B01PropsApi
|
|
|
25
27
|
from .traits.clean_summary import CleanSummaryTrait
|
|
26
28
|
from .traits.dnd import DoNotDisturbTrait
|
|
27
29
|
from .traits.dyad import DyadApi
|
|
30
|
+
from .traits.sound_volume import SoundVolumeTrait
|
|
28
31
|
from .traits.status import StatusTrait
|
|
29
32
|
from .traits.trait import Trait
|
|
30
33
|
from .traits.zeo import ZeoApi
|
|
@@ -112,7 +115,9 @@ class DeviceManager:
|
|
|
112
115
|
await asyncio.gather(*tasks)
|
|
113
116
|
|
|
114
117
|
|
|
115
|
-
def create_home_data_api(
|
|
118
|
+
def create_home_data_api(
|
|
119
|
+
email: str, user_data: UserData, base_url: str | None = None, session: aiohttp.ClientSession | None = None
|
|
120
|
+
) -> HomeDataApi:
|
|
116
121
|
"""Create a home data API wrapper.
|
|
117
122
|
|
|
118
123
|
This function creates a wrapper around the Roborock API client to fetch
|
|
@@ -121,7 +126,7 @@ def create_home_data_api(email: str, user_data: UserData) -> HomeDataApi:
|
|
|
121
126
|
|
|
122
127
|
# Note: This will auto discover the API base URL. This can be improved
|
|
123
128
|
# by caching this next to `UserData` if needed to avoid unnecessary API calls.
|
|
124
|
-
client = RoborockApiClient(email)
|
|
129
|
+
client = RoborockApiClient(username=email, base_url=base_url, session=session)
|
|
125
130
|
|
|
126
131
|
async def home_data_api() -> HomeData:
|
|
127
132
|
return await client.get_home_data_v3(user_data)
|
|
@@ -156,6 +161,7 @@ async def create_device_manager(
|
|
|
156
161
|
traits.append(StatusTrait(product, channel.rpc_channel))
|
|
157
162
|
traits.append(DoNotDisturbTrait(channel.rpc_channel))
|
|
158
163
|
traits.append(CleanSummaryTrait(channel.rpc_channel))
|
|
164
|
+
traits.append(SoundVolumeTrait(channel.rpc_channel))
|
|
159
165
|
case DeviceVersion.A01:
|
|
160
166
|
mqtt_channel = create_mqtt_channel(user_data, mqtt_params, mqtt_session, device)
|
|
161
167
|
match product.category:
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Module for controlling the sound volume of Roborock devices."""
|
|
2
|
+
|
|
3
|
+
from roborock.devices.traits.trait import Trait
|
|
4
|
+
from roborock.devices.v1_rpc_channel import V1RpcChannel
|
|
5
|
+
from roborock.exceptions import RoborockException
|
|
6
|
+
from roborock.roborock_typing import RoborockCommand
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"SoundVolumeTrait",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SoundVolumeTrait(Trait):
|
|
14
|
+
"""Trait for controlling the sound volume of a Roborock device."""
|
|
15
|
+
|
|
16
|
+
name = "sound_volume"
|
|
17
|
+
|
|
18
|
+
def __init__(self, rpc_channel: V1RpcChannel) -> None:
|
|
19
|
+
"""Initialize the SoundVolumeTrait."""
|
|
20
|
+
self._rpc_channel = rpc_channel
|
|
21
|
+
|
|
22
|
+
async def get_volume(self) -> int:
|
|
23
|
+
"""Get the current sound volume of the device."""
|
|
24
|
+
response = await self._rpc_channel.send_command(RoborockCommand.GET_SOUND_VOLUME)
|
|
25
|
+
if not isinstance(response, list) or not response:
|
|
26
|
+
raise RoborockException(f"Unexpected volume format: {response!r}")
|
|
27
|
+
return int(response[0])
|
|
28
|
+
|
|
29
|
+
async def set_volume(self, volume: int) -> None:
|
|
30
|
+
"""Set the sound volume of the device."""
|
|
31
|
+
await self._rpc_channel.send_command(RoborockCommand.CHANGE_SOUND_VOLUME, params=[volume])
|
|
@@ -149,7 +149,10 @@ class PayloadEncodedV1RpcChannel(BaseV1RpcChannel):
|
|
|
149
149
|
return
|
|
150
150
|
_LOGGER.debug("Received response (request_id=%s): %s", self._name, decoded.request_id)
|
|
151
151
|
if decoded.request_id == request_message.request_id:
|
|
152
|
-
|
|
152
|
+
if decoded.api_error:
|
|
153
|
+
future.set_exception(decoded.api_error)
|
|
154
|
+
else:
|
|
155
|
+
future.set_result(decoded.data)
|
|
153
156
|
|
|
154
157
|
unsub = await self._channel.subscribe(find_response)
|
|
155
158
|
try:
|
|
@@ -108,9 +108,18 @@ class ResponseMessage:
|
|
|
108
108
|
data: ResponseData
|
|
109
109
|
"""The data of the response, where the type depends on the command."""
|
|
110
110
|
|
|
111
|
+
api_error: RoborockException | None = None
|
|
112
|
+
"""The API error message of the response if any."""
|
|
113
|
+
|
|
111
114
|
|
|
112
115
|
def decode_rpc_response(message: RoborockMessage) -> ResponseMessage:
|
|
113
|
-
"""Decode a V1 RPC_RESPONSE message.
|
|
116
|
+
"""Decode a V1 RPC_RESPONSE message.
|
|
117
|
+
|
|
118
|
+
This will raise a RoborockException if the message cannot be parsed. A
|
|
119
|
+
response object will be returned even if there is an error in the
|
|
120
|
+
response, as long as we can extract the request ID. This is so we can
|
|
121
|
+
associate an API response with a request even if there was an error.
|
|
122
|
+
"""
|
|
114
123
|
if not message.payload:
|
|
115
124
|
return ResponseMessage(request_id=message.seq, data={})
|
|
116
125
|
try:
|
|
@@ -136,19 +145,26 @@ def decode_rpc_response(message: RoborockMessage) -> ResponseMessage:
|
|
|
136
145
|
) from e
|
|
137
146
|
|
|
138
147
|
request_id: int | None = data_point_response.get("id")
|
|
148
|
+
exc: RoborockException | None = None
|
|
139
149
|
if error := data_point_response.get("error"):
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
exc = RoborockException(error)
|
|
142
151
|
if not (result := data_point_response.get("result")):
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
result
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
exc = RoborockException(f"Invalid V1 message format: missing 'result' in data point for {message.payload!r}")
|
|
153
|
+
else:
|
|
154
|
+
_LOGGER.debug("Decoded V1 message result: %s", result)
|
|
155
|
+
if isinstance(result, str):
|
|
156
|
+
if result == "unknown_method":
|
|
157
|
+
exc = RoborockException("The method called is not recognized by the device.")
|
|
158
|
+
elif result != "ok":
|
|
159
|
+
exc = RoborockException(f"Unexpected API Result: {result}")
|
|
160
|
+
result = {}
|
|
161
|
+
if not isinstance(result, (dict, list, int)):
|
|
162
|
+
raise RoborockException(
|
|
163
|
+
f"Invalid V1 message format: 'result' was unexpected type {type(result)}. {message.payload!r}"
|
|
164
|
+
)
|
|
165
|
+
if not request_id and exc:
|
|
166
|
+
raise exc
|
|
167
|
+
return ResponseMessage(request_id=request_id, data=result, api_error=exc)
|
|
152
168
|
|
|
153
169
|
|
|
154
170
|
@dataclass
|
|
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
|
|
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.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/roborock_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_1_apis/roborock_mqtt_client_v1.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_roborock-2.43.0 → python_roborock-2.44.1}/roborock/version_a01_apis/roborock_client_a01.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|