roborock.devices.traits.b01.q7
Traits for Q7 B01 devices. Potentially other devices may fall into this category in the future.
1"""Traits for Q7 B01 devices. 2Potentially other devices may fall into this category in the future.""" 3 4from typing import Any 5 6from roborock import B01Props 7from roborock.data.b01_q7.b01_q7_code_mappings import ( 8 CleanPathPreferenceMapping, 9 CleanRepeatMapping, 10 CleanTaskTypeMapping, 11 CleanTypeMapping, 12 SCDeviceCleanParam, 13 SCWindMapping, 14 WaterLevelMapping, 15) 16from roborock.devices.rpc.b01_q7_channel import send_decoded_command 17from roborock.devices.traits import Trait 18from roborock.devices.transport.mqtt_channel import MqttChannel 19from roborock.protocols.b01_q7_protocol import CommandType, ParamsType, Q7RequestMessage 20from roborock.roborock_message import RoborockB01Props 21from roborock.roborock_typing import RoborockB01Q7Methods 22 23from .clean_summary import CleanSummaryTrait 24 25__all__ = [ 26 "Q7PropertiesApi", 27 "CleanSummaryTrait", 28] 29 30 31class Q7PropertiesApi(Trait): 32 """API for interacting with B01 devices.""" 33 34 clean_summary: CleanSummaryTrait 35 """Trait for clean records / clean summary (Q7 `service.get_record_list`).""" 36 37 def __init__(self, channel: MqttChannel) -> None: 38 """Initialize the B01Props API.""" 39 self._channel = channel 40 self.clean_summary = CleanSummaryTrait(channel) 41 42 async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None: 43 """Query the device for the values of the given Q7 properties.""" 44 result = await self.send( 45 RoborockB01Q7Methods.GET_PROP, 46 {"property": props}, 47 ) 48 if not isinstance(result, dict): 49 raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}") 50 return B01Props.from_dict(result) 51 52 async def set_prop(self, prop: RoborockB01Props, value: Any) -> None: 53 """Set a property on the device.""" 54 await self.send( 55 command=RoborockB01Q7Methods.SET_PROP, 56 params={prop: value}, 57 ) 58 59 async def set_fan_speed(self, fan_speed: SCWindMapping) -> None: 60 """Set the fan speed (wind).""" 61 await self.set_prop(RoborockB01Props.WIND, fan_speed.code) 62 63 async def set_water_level(self, water_level: WaterLevelMapping) -> None: 64 """Set the water level (water).""" 65 await self.set_prop(RoborockB01Props.WATER, water_level.code) 66 67 async def set_mode(self, mode: CleanTypeMapping) -> None: 68 """Set the cleaning mode (vacuum, mop, or vacuum and mop).""" 69 await self.set_prop(RoborockB01Props.MODE, mode.code) 70 71 async def set_clean_path_preference(self, preference: CleanPathPreferenceMapping) -> None: 72 """Set the cleaning path preference (route).""" 73 await self.set_prop(RoborockB01Props.CLEAN_PATH_PREFERENCE, preference.code) 74 75 async def set_repeat_state(self, repeat: CleanRepeatMapping) -> None: 76 """Set the cleaning repeat state (cycles).""" 77 await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code) 78 79 async def start_clean(self) -> None: 80 """Start cleaning.""" 81 await self.send( 82 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 83 params={ 84 "clean_type": CleanTaskTypeMapping.ALL.code, 85 "ctrl_value": SCDeviceCleanParam.START.code, 86 "room_ids": [], 87 }, 88 ) 89 90 async def pause_clean(self) -> None: 91 """Pause cleaning.""" 92 await self.send( 93 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 94 params={ 95 "clean_type": CleanTaskTypeMapping.ALL.code, 96 "ctrl_value": SCDeviceCleanParam.PAUSE.code, 97 "room_ids": [], 98 }, 99 ) 100 101 async def stop_clean(self) -> None: 102 """Stop cleaning.""" 103 await self.send( 104 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 105 params={ 106 "clean_type": CleanTaskTypeMapping.ALL.code, 107 "ctrl_value": SCDeviceCleanParam.STOP.code, 108 "room_ids": [], 109 }, 110 ) 111 112 async def return_to_dock(self) -> None: 113 """Return to dock.""" 114 await self.send( 115 command=RoborockB01Q7Methods.START_RECHARGE, 116 params={}, 117 ) 118 119 async def find_me(self) -> None: 120 """Locate the robot.""" 121 await self.send( 122 command=RoborockB01Q7Methods.FIND_DEVICE, 123 params={}, 124 ) 125 126 async def send(self, command: CommandType, params: ParamsType) -> Any: 127 """Send a command to the device.""" 128 return await send_decoded_command( 129 self._channel, 130 Q7RequestMessage(dps=10000, command=command, params=params), 131 ) 132 133 134def create(channel: MqttChannel) -> Q7PropertiesApi: 135 """Create traits for B01 devices.""" 136 return Q7PropertiesApi(channel)
32class Q7PropertiesApi(Trait): 33 """API for interacting with B01 devices.""" 34 35 clean_summary: CleanSummaryTrait 36 """Trait for clean records / clean summary (Q7 `service.get_record_list`).""" 37 38 def __init__(self, channel: MqttChannel) -> None: 39 """Initialize the B01Props API.""" 40 self._channel = channel 41 self.clean_summary = CleanSummaryTrait(channel) 42 43 async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None: 44 """Query the device for the values of the given Q7 properties.""" 45 result = await self.send( 46 RoborockB01Q7Methods.GET_PROP, 47 {"property": props}, 48 ) 49 if not isinstance(result, dict): 50 raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}") 51 return B01Props.from_dict(result) 52 53 async def set_prop(self, prop: RoborockB01Props, value: Any) -> None: 54 """Set a property on the device.""" 55 await self.send( 56 command=RoborockB01Q7Methods.SET_PROP, 57 params={prop: value}, 58 ) 59 60 async def set_fan_speed(self, fan_speed: SCWindMapping) -> None: 61 """Set the fan speed (wind).""" 62 await self.set_prop(RoborockB01Props.WIND, fan_speed.code) 63 64 async def set_water_level(self, water_level: WaterLevelMapping) -> None: 65 """Set the water level (water).""" 66 await self.set_prop(RoborockB01Props.WATER, water_level.code) 67 68 async def set_mode(self, mode: CleanTypeMapping) -> None: 69 """Set the cleaning mode (vacuum, mop, or vacuum and mop).""" 70 await self.set_prop(RoborockB01Props.MODE, mode.code) 71 72 async def set_clean_path_preference(self, preference: CleanPathPreferenceMapping) -> None: 73 """Set the cleaning path preference (route).""" 74 await self.set_prop(RoborockB01Props.CLEAN_PATH_PREFERENCE, preference.code) 75 76 async def set_repeat_state(self, repeat: CleanRepeatMapping) -> None: 77 """Set the cleaning repeat state (cycles).""" 78 await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code) 79 80 async def start_clean(self) -> None: 81 """Start cleaning.""" 82 await self.send( 83 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 84 params={ 85 "clean_type": CleanTaskTypeMapping.ALL.code, 86 "ctrl_value": SCDeviceCleanParam.START.code, 87 "room_ids": [], 88 }, 89 ) 90 91 async def pause_clean(self) -> None: 92 """Pause cleaning.""" 93 await self.send( 94 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 95 params={ 96 "clean_type": CleanTaskTypeMapping.ALL.code, 97 "ctrl_value": SCDeviceCleanParam.PAUSE.code, 98 "room_ids": [], 99 }, 100 ) 101 102 async def stop_clean(self) -> None: 103 """Stop cleaning.""" 104 await self.send( 105 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 106 params={ 107 "clean_type": CleanTaskTypeMapping.ALL.code, 108 "ctrl_value": SCDeviceCleanParam.STOP.code, 109 "room_ids": [], 110 }, 111 ) 112 113 async def return_to_dock(self) -> None: 114 """Return to dock.""" 115 await self.send( 116 command=RoborockB01Q7Methods.START_RECHARGE, 117 params={}, 118 ) 119 120 async def find_me(self) -> None: 121 """Locate the robot.""" 122 await self.send( 123 command=RoborockB01Q7Methods.FIND_DEVICE, 124 params={}, 125 ) 126 127 async def send(self, command: CommandType, params: ParamsType) -> Any: 128 """Send a command to the device.""" 129 return await send_decoded_command( 130 self._channel, 131 Q7RequestMessage(dps=10000, command=command, params=params), 132 )
API for interacting with B01 devices.
38 def __init__(self, channel: MqttChannel) -> None: 39 """Initialize the B01Props API.""" 40 self._channel = channel 41 self.clean_summary = CleanSummaryTrait(channel)
Initialize the B01Props API.
Trait for clean records / clean summary (Q7 service.get_record_list).
43 async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None: 44 """Query the device for the values of the given Q7 properties.""" 45 result = await self.send( 46 RoborockB01Q7Methods.GET_PROP, 47 {"property": props}, 48 ) 49 if not isinstance(result, dict): 50 raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}") 51 return B01Props.from_dict(result)
Query the device for the values of the given Q7 properties.
53 async def set_prop(self, prop: RoborockB01Props, value: Any) -> None: 54 """Set a property on the device.""" 55 await self.send( 56 command=RoborockB01Q7Methods.SET_PROP, 57 params={prop: value}, 58 )
Set a property on the device.
60 async def set_fan_speed(self, fan_speed: SCWindMapping) -> None: 61 """Set the fan speed (wind).""" 62 await self.set_prop(RoborockB01Props.WIND, fan_speed.code)
Set the fan speed (wind).
64 async def set_water_level(self, water_level: WaterLevelMapping) -> None: 65 """Set the water level (water).""" 66 await self.set_prop(RoborockB01Props.WATER, water_level.code)
Set the water level (water).
68 async def set_mode(self, mode: CleanTypeMapping) -> None: 69 """Set the cleaning mode (vacuum, mop, or vacuum and mop).""" 70 await self.set_prop(RoborockB01Props.MODE, mode.code)
Set the cleaning mode (vacuum, mop, or vacuum and mop).
72 async def set_clean_path_preference(self, preference: CleanPathPreferenceMapping) -> None: 73 """Set the cleaning path preference (route).""" 74 await self.set_prop(RoborockB01Props.CLEAN_PATH_PREFERENCE, preference.code)
Set the cleaning path preference (route).
76 async def set_repeat_state(self, repeat: CleanRepeatMapping) -> None: 77 """Set the cleaning repeat state (cycles).""" 78 await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code)
Set the cleaning repeat state (cycles).
80 async def start_clean(self) -> None: 81 """Start cleaning.""" 82 await self.send( 83 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 84 params={ 85 "clean_type": CleanTaskTypeMapping.ALL.code, 86 "ctrl_value": SCDeviceCleanParam.START.code, 87 "room_ids": [], 88 }, 89 )
Start cleaning.
91 async def pause_clean(self) -> None: 92 """Pause cleaning.""" 93 await self.send( 94 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 95 params={ 96 "clean_type": CleanTaskTypeMapping.ALL.code, 97 "ctrl_value": SCDeviceCleanParam.PAUSE.code, 98 "room_ids": [], 99 }, 100 )
Pause cleaning.
102 async def stop_clean(self) -> None: 103 """Stop cleaning.""" 104 await self.send( 105 command=RoborockB01Q7Methods.SET_ROOM_CLEAN, 106 params={ 107 "clean_type": CleanTaskTypeMapping.ALL.code, 108 "ctrl_value": SCDeviceCleanParam.STOP.code, 109 "room_ids": [], 110 }, 111 )
Stop cleaning.
113 async def return_to_dock(self) -> None: 114 """Return to dock.""" 115 await self.send( 116 command=RoborockB01Q7Methods.START_RECHARGE, 117 params={}, 118 )
Return to dock.
120 async def find_me(self) -> None: 121 """Locate the robot.""" 122 await self.send( 123 command=RoborockB01Q7Methods.FIND_DEVICE, 124 params={}, 125 )
Locate the robot.
127 async def send(self, command: CommandType, params: ParamsType) -> Any: 128 """Send a command to the device.""" 129 return await send_decoded_command( 130 self._channel, 131 Q7RequestMessage(dps=10000, command=command, params=params), 132 )
Send a command to the device.
27class CleanSummaryTrait(CleanRecordSummary, Trait): 28 """B01/Q7 clean summary + clean record access (via record list service).""" 29 30 def __init__(self, channel: MqttChannel) -> None: 31 """Initialize the clean summary trait. 32 33 Args: 34 channel: MQTT channel used to communicate with the device. 35 """ 36 super().__init__() 37 self._channel = channel 38 39 async def refresh(self) -> None: 40 """Refresh totals and last record detail from the device.""" 41 record_list = await self._get_record_list() 42 43 self.total_time = record_list.total_time 44 self.total_area = record_list.total_area 45 self.total_count = record_list.total_count 46 47 details = await self._get_clean_record_details(record_list=record_list) 48 self.last_record_detail = details[0] if details else None 49 50 async def _get_record_list(self) -> CleanRecordList: 51 """Fetch the raw device clean record list (`service.get_record_list`).""" 52 result = await send_decoded_command( 53 self._channel, 54 Q7RequestMessage(dps=10000, command=RoborockB01Q7Methods.GET_RECORD_LIST, params={}), 55 ) 56 57 if not isinstance(result, dict): 58 raise TypeError(f"Unexpected response type for GET_RECORD_LIST: {type(result).__name__}: {result!r}") 59 return CleanRecordList.from_dict(result) 60 61 async def _get_clean_record_details(self, *, record_list: CleanRecordList) -> list[CleanRecordDetail]: 62 """Return parsed record detail objects (newest-first).""" 63 details: list[CleanRecordDetail] = [] 64 for item in record_list.record_list: 65 try: 66 parsed = item.detail_parsed 67 except RoborockException as ex: 68 # Rather than failing if something goes wrong here, we should fail and log to tell the user. 69 _LOGGER.debug("Failed to parse record detail: %s", ex) 70 continue 71 if parsed is not None: 72 details.append(parsed) 73 74 # The server returns the newest record at the end of record_list; reverse so newest is first (index 0). 75 details.reverse() 76 return details
B01/Q7 clean summary + clean record access (via record list service).
30 def __init__(self, channel: MqttChannel) -> None: 31 """Initialize the clean summary trait. 32 33 Args: 34 channel: MQTT channel used to communicate with the device. 35 """ 36 super().__init__() 37 self._channel = channel
Initialize the clean summary trait.
Args: channel: MQTT channel used to communicate with the device.
39 async def refresh(self) -> None: 40 """Refresh totals and last record detail from the device.""" 41 record_list = await self._get_record_list() 42 43 self.total_time = record_list.total_time 44 self.total_area = record_list.total_area 45 self.total_count = record_list.total_count 46 47 details = await self._get_clean_record_details(record_list=record_list) 48 self.last_record_detail = details[0] if details else None
Refresh totals and last record detail from the device.