-
Notifications
You must be signed in to change notification settings - Fork 74
feat: add some basic setters for q7 #690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
dccbe6b
5c16930
5db7ae4
e6fc646
d026b67
900ed38
80f9875
8680692
eda0a93
080d673
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,12 +28,15 @@ async def send_decoded_command( | |
| dps: int, | ||
| command: CommandType, | ||
| params: ParamsType, | ||
| ) -> dict[str, Any]: | ||
| """Send a command on the MQTT channel and get a decoded response.""" | ||
| ) -> Any: | ||
| """Send a command on the MQTT channel and get a decoded response. | ||
|
|
||
| Note: B01 "set" commands may return a scalar (e.g. 0/"ok") rather than a dict. | ||
| """ | ||
| _LOGGER.debug("Sending MQTT command: %s", params) | ||
| msg_id = str(get_next_int(100000000000, 999999999999)) | ||
| roborock_message = encode_mqtt_payload(dps, command, params, msg_id) | ||
| future: asyncio.Future[dict[str, Any]] = asyncio.get_running_loop().create_future() | ||
| future: asyncio.Future[Any] = asyncio.get_running_loop().create_future() | ||
|
|
||
| def find_response(response_message: RoborockMessage) -> None: | ||
| """Handle incoming messages and resolve the future.""" | ||
|
|
@@ -58,20 +61,24 @@ def find_response(response_message: RoborockMessage) -> None: | |
| if isinstance(inner, dict) and inner.get("msgId") == msg_id: | ||
| _LOGGER.debug("Received query response: %s", inner) | ||
| data = inner.get("data") | ||
| # All get commands should be dicts | ||
| if command.endswith(".get") and not isinstance(data, dict): | ||
| if not future.done(): | ||
| future.set_exception(RoborockException("Unexpected data type for response")) | ||
| return | ||
|
Comment on lines
+84
to
+93
|
||
| if not future.done(): | ||
| if isinstance(data, dict): | ||
| future.set_result(data) | ||
| else: | ||
| future.set_exception(RoborockException(f"Unexpected data type for response: {data}")) | ||
| future.set_result(data) | ||
|
|
||
| unsub = await mqtt_channel.subscribe(find_response) | ||
|
|
||
| _LOGGER.debug("Sending MQTT message: %s", roborock_message) | ||
| try: | ||
| await mqtt_channel.publish(roborock_message) | ||
| try: | ||
| return await asyncio.wait_for(future, timeout=_TIMEOUT) | ||
| except TimeoutError as ex: | ||
| raise RoborockException(f"Command timed out after {_TIMEOUT}s") from ex | ||
| return await asyncio.wait_for(future, timeout=_TIMEOUT) | ||
| except TimeoutError as ex: | ||
| raise RoborockException(f"Command timed out after {_TIMEOUT}s") from ex | ||
| except Exception as ex: | ||
|
Lash-L marked this conversation as resolved.
|
||
| _LOGGER.exception("Error sending decoded command: %s", ex) | ||
|
Lash-L marked this conversation as resolved.
Outdated
|
||
| raise | ||
| finally: | ||
| unsub() | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,15 @@ | ||||||
| """Traits for Q7 B01 devices. | ||||||
| Potentially other devices may fall into this category in the future.""" | ||||||
|
|
||||||
| from typing import Any | ||||||
|
|
||||||
| from roborock import B01Props | ||||||
| from roborock.data.b01_q7.b01_q7_code_mappings import ( | ||||||
| CleanTaskTypeMapping, | ||||||
| SCDeviceCleanParam, | ||||||
| SCWindMapping, | ||||||
| WaterLevelMapping, | ||||||
| ) | ||||||
| from roborock.devices.b01_channel import send_decoded_command | ||||||
| from roborock.devices.mqtt_channel import MqttChannel | ||||||
| from roborock.devices.traits import Trait | ||||||
|
|
@@ -20,13 +28,86 @@ def __init__(self, channel: MqttChannel) -> None: | |||||
| """Initialize the B01Props API.""" | ||||||
| self._channel = channel | ||||||
|
|
||||||
| async def send(self, command: RoborockB01Q7Methods, params: dict) -> Any: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add typing
Suggested change
Lash-L marked this conversation as resolved.
Outdated
|
||||||
| return await send_decoded_command( | ||||||
| self._channel, | ||||||
| dps=10000, | ||||||
| command=command, | ||||||
| params=params, | ||||||
| ) | ||||||
|
|
||||||
| async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None: | ||||||
| """Query the device for the values of the given Q7 properties.""" | ||||||
| result = await send_decoded_command( | ||||||
| self._channel, dps=10000, command=RoborockB01Q7Methods.GET_PROP, params={"property": props} | ||||||
| result = await self.send( | ||||||
| RoborockB01Q7Methods.GET_PROP, | ||||||
| {"property": props}, | ||||||
| ) | ||||||
| if not isinstance(result, dict): | ||||||
| raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}") | ||||||
| return B01Props.from_dict(result) | ||||||
|
|
||||||
| async def set_prop(self, prop: RoborockB01Props, value: Any) -> Any: | ||||||
|
Lash-L marked this conversation as resolved.
Outdated
|
||||||
| """Set a property on the device.""" | ||||||
| await self.send( | ||||||
| command=RoborockB01Q7Methods.SET_PROP, | ||||||
| params={prop: value}, | ||||||
| ) | ||||||
|
|
||||||
| async def set_fan_speed(self, fan_speed: SCWindMapping) -> Any: | ||||||
|
Lash-L marked this conversation as resolved.
Outdated
|
||||||
| """Set the fan speed (wind).""" | ||||||
| return await self.set_prop(RoborockB01Props.WIND, fan_speed.code) | ||||||
|
|
||||||
| async def set_water_level(self, water_level: WaterLevelMapping) -> Any: | ||||||
| """Set the water level (water).""" | ||||||
| return await self.set_prop(RoborockB01Props.WATER, water_level.code) | ||||||
|
|
||||||
| async def start_clean(self) -> Any: | ||||||
| """Start cleaning.""" | ||||||
| return await self.send( | ||||||
| command=RoborockB01Q7Methods.SET_ROOM_CLEAN, | ||||||
| params={ | ||||||
| "clean_type": CleanTaskTypeMapping.ALL.code, | ||||||
| "ctrl_value": SCDeviceCleanParam.START.code, | ||||||
| "room_ids": [], | ||||||
| }, | ||||||
| ) | ||||||
|
|
||||||
| async def pause_clean(self) -> Any: | ||||||
| """Pause cleaning.""" | ||||||
| return await self.send( | ||||||
| command=RoborockB01Q7Methods.SET_ROOM_CLEAN, | ||||||
| params={ | ||||||
| "clean_type": CleanTaskTypeMapping.ALL.code, | ||||||
| "ctrl_value": SCDeviceCleanParam.PAUSE.code, | ||||||
| "room_ids": [], | ||||||
| }, | ||||||
| ) | ||||||
|
|
||||||
| async def stop_clean(self) -> Any: | ||||||
| """Stop cleaning.""" | ||||||
| return await self.send( | ||||||
| command=RoborockB01Q7Methods.SET_ROOM_CLEAN, | ||||||
| params={ | ||||||
| "clean_type": CleanTaskTypeMapping.ALL.code, | ||||||
| "ctrl_value": SCDeviceCleanParam.STOP.code, | ||||||
| "room_ids": [], | ||||||
| }, | ||||||
| ) | ||||||
|
|
||||||
| async def return_to_dock(self) -> Any: | ||||||
| """Return to dock.""" | ||||||
| return await self.send( | ||||||
| command=RoborockB01Q7Methods.START_RECHARGE, | ||||||
| params={}, | ||||||
| ) | ||||||
|
|
||||||
| async def find_me(self) -> Any: | ||||||
| """Locate the robot.""" | ||||||
| return await self.send( | ||||||
| command=RoborockB01Q7Methods.FIND_DEVICE, | ||||||
| params={}, | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
| def create(channel: MqttChannel) -> Q7PropertiesApi: | ||||||
| """Create traits for B01 devices.""" | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.