roborock.devices.traits.b01

Traits for B01 devices.

 1"""Traits for B01 devices."""
 2
 3from . import q7, q10
 4from .q7 import Q7PropertiesApi
 5from .q10 import Q10PropertiesApi
 6
 7__all__ = [
 8    "Q7PropertiesApi",
 9    "Q10PropertiesApi",
10    "q7",
11    "q10",
12]
class Q7PropertiesApi(roborock.devices.traits.Trait):
 42class Q7PropertiesApi(Trait):
 43    """API for interacting with B01 Q7 devices."""
 44
 45    clean_summary: CleanSummaryTrait
 46    """Trait for clean records / clean summary (Q7 `service.get_record_list`)."""
 47
 48    map: MapTrait
 49    """Trait for map list metadata + raw map payload retrieval."""
 50
 51    map_content: MapContentTrait
 52    """Trait for fetching parsed current map content."""
 53
 54    def __init__(
 55        self,
 56        rpc_channel: Q7RpcChannel,
 57        map_rpc_channel: Q7MapRpcChannel,
 58        device: HomeDataDevice,
 59        product: HomeDataProduct,
 60    ) -> None:
 61        """Initialize the Q7 API."""
 62        self._rpc_channel = rpc_channel
 63        self._map_rpc_channel = map_rpc_channel
 64        self._device = device
 65        self._product = product
 66
 67        if not device.sn or not product.model:
 68            raise ValueError("B01 Q7 map content requires device serial number and product model metadata")
 69
 70        self.clean_summary = CleanSummaryTrait(rpc_channel)
 71        self.map = MapTrait(rpc_channel)
 72        self.map_content = MapContentTrait(
 73            self._map_rpc_channel,
 74            self.map,
 75        )
 76        self._unsub_map_pushes: Callable[[], None] | None = None
 77
 78    async def start(self) -> None:
 79        """Start listening for unsolicited map pushes from the device."""
 80        if self._unsub_map_pushes is not None:
 81            return
 82        self._unsub_map_pushes = await self._map_rpc_channel.subscribe_map_pushes(self.map_content.update_from_push)
 83
 84    async def close(self) -> None:
 85        """Stop listening for unsolicited map pushes."""
 86        if self._unsub_map_pushes is not None:
 87            self._unsub_map_pushes()
 88            self._unsub_map_pushes = None
 89
 90    async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None:
 91        """Query the device for the values of the given Q7 properties."""
 92        result = await self.send(
 93            RoborockB01Q7Methods.GET_PROP,
 94            {"property": props},
 95        )
 96        if not isinstance(result, dict):
 97            raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}")
 98        return B01Props.from_dict(result)
 99
100    async def set_prop(self, prop: RoborockB01Props, value: Any) -> None:
101        """Set a property on the device."""
102        await self.send(
103            command=RoborockB01Q7Methods.SET_PROP,
104            params={prop: value},
105        )
106
107    async def set_fan_speed(self, fan_speed: SCWindMapping) -> None:
108        """Set the fan speed (wind)."""
109        await self.set_prop(RoborockB01Props.WIND, fan_speed.code)
110
111    async def set_water_level(self, water_level: WaterLevelMapping) -> None:
112        """Set the water level (water)."""
113        await self.set_prop(RoborockB01Props.WATER, water_level.code)
114
115    async def set_mode(self, mode: CleanTypeMapping) -> None:
116        """Set the cleaning mode (vacuum, mop, or vacuum and mop)."""
117        await self.set_prop(RoborockB01Props.MODE, mode.code)
118
119    async def set_clean_path_preference(self, preference: CleanPathPreferenceMapping) -> None:
120        """Set the cleaning path preference (route)."""
121        await self.set_prop(RoborockB01Props.CLEAN_PATH_PREFERENCE, preference.code)
122
123    async def set_repeat_state(self, repeat: CleanRepeatMapping) -> None:
124        """Set the cleaning repeat state (cycles)."""
125        await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code)
126
127    async def set_volume(self, volume: int) -> None:
128        """Set the robot voice volume (0-100)."""
129        await self.set_prop(RoborockB01Props.VOLUME, volume)
130
131    async def set_child_lock(self, enabled: bool) -> None:
132        """Enable or disable the child lock."""
133        await self.set_prop(RoborockB01Props.CHILD_LOCK, int(enabled))
134
135    async def set_dust_collection(self, enabled: bool) -> None:
136        """Enable or disable automatic dust collection at the dock."""
137        await self.set_prop(RoborockB01Props.DUST_AUTO_STATE, int(enabled))
138
139    async def set_dust_collection_frequency(self, frequency: int) -> None:
140        """Set how often the dock auto-empties, in cleans per emptying (1 = every clean)."""
141        if frequency < 1:
142            raise ValueError(f"frequency must be a positive number of cleans, got {frequency}")
143        await self.set_prop(RoborockB01Props.DUST_FREQUENCY, frequency)
144
145    async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None:
146        """Configure do-not-disturb.
147
148        The device expects all three values together via ``service.set_quiet_time``
149        (individual ``prop.set`` calls are ignored). ``begin_time``/``end_time`` are
150        minutes since midnight and must be in the range 0-1439 (inclusive).
151
152        Ranges that cross midnight are supported by passing a ``begin_time`` that is
153        greater than ``end_time`` (e.g. 22:00-07:00 is ``begin_time=1320``,
154        ``end_time=420``).
155        """
156        for name, value in (("begin_time", begin_time), ("end_time", end_time)):
157            if not 0 <= value <= 1439:
158                raise ValueError(f"{name} must be between 0 and 1439 minutes since midnight, got {value}")
159        await self.send(
160            RoborockB01Q7Methods.SET_QUIET_TIME,
161            {
162                "is_open": int(enabled),
163                "quiet_begin_time": begin_time,
164                "quiet_end_time": end_time,
165            },
166        )
167
168    async def set_button_lights(self, enabled: bool) -> None:
169        """Enable or disable the button/panel lights."""
170        await self.set_prop(RoborockB01Props.LIGHT_MODE, int(enabled))
171
172    async def start_clean(self) -> None:
173        """Start cleaning."""
174        await self.send(
175            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
176            params={
177                "clean_type": CleanTaskTypeMapping.ALL.code,
178                "ctrl_value": SCDeviceCleanParam.START.code,
179                "room_ids": [],
180            },
181        )
182
183    async def clean_segments(self, segment_ids: list[int]) -> None:
184        """Start segment cleaning for the given ids (Q7 uses room ids)."""
185        await self.send(
186            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
187            params={
188                "clean_type": CleanTaskTypeMapping.ROOM.code,
189                "ctrl_value": SCDeviceCleanParam.START.code,
190                "room_ids": segment_ids,
191            },
192        )
193
194    async def pause_clean(self) -> None:
195        """Pause cleaning."""
196        await self.send(
197            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
198            params={
199                "clean_type": CleanTaskTypeMapping.ALL.code,
200                "ctrl_value": SCDeviceCleanParam.PAUSE.code,
201                "room_ids": [],
202            },
203        )
204
205    async def stop_clean(self) -> None:
206        """Stop cleaning."""
207        await self.send(
208            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
209            params={
210                "clean_type": CleanTaskTypeMapping.ALL.code,
211                "ctrl_value": SCDeviceCleanParam.STOP.code,
212                "room_ids": [],
213            },
214        )
215
216    async def return_to_dock(self) -> None:
217        """Return to dock."""
218        await self.send(
219            command=RoborockB01Q7Methods.START_RECHARGE,
220            params={},
221        )
222
223    async def find_me(self) -> None:
224        """Locate the robot."""
225        await self.send(
226            command=RoborockB01Q7Methods.FIND_DEVICE,
227            params={},
228        )
229
230    async def send(self, command: CommandType, params: ParamsType) -> Any:
231        """Send a command to the device."""
232        return await self._rpc_channel.send_command(command, params)

API for interacting with B01 Q7 devices.

Q7PropertiesApi( rpc_channel: roborock.devices.rpc.b01_q7_channel.Q7RpcChannel, map_rpc_channel: roborock.devices.rpc.b01_q7_channel.Q7MapRpcChannel, device: roborock.data.containers.HomeDataDevice, product: roborock.data.containers.HomeDataProduct)
54    def __init__(
55        self,
56        rpc_channel: Q7RpcChannel,
57        map_rpc_channel: Q7MapRpcChannel,
58        device: HomeDataDevice,
59        product: HomeDataProduct,
60    ) -> None:
61        """Initialize the Q7 API."""
62        self._rpc_channel = rpc_channel
63        self._map_rpc_channel = map_rpc_channel
64        self._device = device
65        self._product = product
66
67        if not device.sn or not product.model:
68            raise ValueError("B01 Q7 map content requires device serial number and product model metadata")
69
70        self.clean_summary = CleanSummaryTrait(rpc_channel)
71        self.map = MapTrait(rpc_channel)
72        self.map_content = MapContentTrait(
73            self._map_rpc_channel,
74            self.map,
75        )
76        self._unsub_map_pushes: Callable[[], None] | None = None

Initialize the Q7 API.

Trait for clean records / clean summary (Q7 service.get_record_list).

Trait for map list metadata + raw map payload retrieval.

Trait for fetching parsed current map content.

async def start(self) -> None:
78    async def start(self) -> None:
79        """Start listening for unsolicited map pushes from the device."""
80        if self._unsub_map_pushes is not None:
81            return
82        self._unsub_map_pushes = await self._map_rpc_channel.subscribe_map_pushes(self.map_content.update_from_push)

Start listening for unsolicited map pushes from the device.

async def close(self) -> None:
84    async def close(self) -> None:
85        """Stop listening for unsolicited map pushes."""
86        if self._unsub_map_pushes is not None:
87            self._unsub_map_pushes()
88            self._unsub_map_pushes = None

Stop listening for unsolicited map pushes.

async def query_values( self, props: list[roborock.roborock_message.RoborockB01Props]) -> roborock.data.b01_q7.b01_q7_containers.B01Props | None:
90    async def query_values(self, props: list[RoborockB01Props]) -> B01Props | None:
91        """Query the device for the values of the given Q7 properties."""
92        result = await self.send(
93            RoborockB01Q7Methods.GET_PROP,
94            {"property": props},
95        )
96        if not isinstance(result, dict):
97            raise TypeError(f"Unexpected response type for GET_PROP: {type(result).__name__}: {result!r}")
98        return B01Props.from_dict(result)

Query the device for the values of the given Q7 properties.

async def set_prop( self, prop: roborock.roborock_message.RoborockB01Props, value: Any) -> None:
100    async def set_prop(self, prop: RoborockB01Props, value: Any) -> None:
101        """Set a property on the device."""
102        await self.send(
103            command=RoborockB01Q7Methods.SET_PROP,
104            params={prop: value},
105        )

Set a property on the device.

async def set_fan_speed( self, fan_speed: roborock.data.b01_q7.b01_q7_code_mappings.SCWindMapping) -> None:
107    async def set_fan_speed(self, fan_speed: SCWindMapping) -> None:
108        """Set the fan speed (wind)."""
109        await self.set_prop(RoborockB01Props.WIND, fan_speed.code)

Set the fan speed (wind).

async def set_water_level( self, water_level: roborock.data.b01_q7.b01_q7_code_mappings.WaterLevelMapping) -> None:
111    async def set_water_level(self, water_level: WaterLevelMapping) -> None:
112        """Set the water level (water)."""
113        await self.set_prop(RoborockB01Props.WATER, water_level.code)

Set the water level (water).

async def set_mode( self, mode: roborock.data.b01_q7.b01_q7_code_mappings.CleanTypeMapping) -> None:
115    async def set_mode(self, mode: CleanTypeMapping) -> None:
116        """Set the cleaning mode (vacuum, mop, or vacuum and mop)."""
117        await self.set_prop(RoborockB01Props.MODE, mode.code)

Set the cleaning mode (vacuum, mop, or vacuum and mop).

async def set_clean_path_preference( self, preference: roborock.data.b01_q7.b01_q7_code_mappings.CleanPathPreferenceMapping) -> None:
119    async def set_clean_path_preference(self, preference: CleanPathPreferenceMapping) -> None:
120        """Set the cleaning path preference (route)."""
121        await self.set_prop(RoborockB01Props.CLEAN_PATH_PREFERENCE, preference.code)

Set the cleaning path preference (route).

async def set_repeat_state( self, repeat: roborock.data.b01_q7.b01_q7_code_mappings.CleanRepeatMapping) -> None:
123    async def set_repeat_state(self, repeat: CleanRepeatMapping) -> None:
124        """Set the cleaning repeat state (cycles)."""
125        await self.set_prop(RoborockB01Props.REPEAT_STATE, repeat.code)

Set the cleaning repeat state (cycles).

async def set_volume(self, volume: int) -> None:
127    async def set_volume(self, volume: int) -> None:
128        """Set the robot voice volume (0-100)."""
129        await self.set_prop(RoborockB01Props.VOLUME, volume)

Set the robot voice volume (0-100).

async def set_child_lock(self, enabled: bool) -> None:
131    async def set_child_lock(self, enabled: bool) -> None:
132        """Enable or disable the child lock."""
133        await self.set_prop(RoborockB01Props.CHILD_LOCK, int(enabled))

Enable or disable the child lock.

async def set_dust_collection(self, enabled: bool) -> None:
135    async def set_dust_collection(self, enabled: bool) -> None:
136        """Enable or disable automatic dust collection at the dock."""
137        await self.set_prop(RoborockB01Props.DUST_AUTO_STATE, int(enabled))

Enable or disable automatic dust collection at the dock.

async def set_dust_collection_frequency(self, frequency: int) -> None:
139    async def set_dust_collection_frequency(self, frequency: int) -> None:
140        """Set how often the dock auto-empties, in cleans per emptying (1 = every clean)."""
141        if frequency < 1:
142            raise ValueError(f"frequency must be a positive number of cleans, got {frequency}")
143        await self.set_prop(RoborockB01Props.DUST_FREQUENCY, frequency)

Set how often the dock auto-empties, in cleans per emptying (1 = every clean).

async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None:
145    async def set_do_not_disturb(self, enabled: bool, begin_time: int, end_time: int) -> None:
146        """Configure do-not-disturb.
147
148        The device expects all three values together via ``service.set_quiet_time``
149        (individual ``prop.set`` calls are ignored). ``begin_time``/``end_time`` are
150        minutes since midnight and must be in the range 0-1439 (inclusive).
151
152        Ranges that cross midnight are supported by passing a ``begin_time`` that is
153        greater than ``end_time`` (e.g. 22:00-07:00 is ``begin_time=1320``,
154        ``end_time=420``).
155        """
156        for name, value in (("begin_time", begin_time), ("end_time", end_time)):
157            if not 0 <= value <= 1439:
158                raise ValueError(f"{name} must be between 0 and 1439 minutes since midnight, got {value}")
159        await self.send(
160            RoborockB01Q7Methods.SET_QUIET_TIME,
161            {
162                "is_open": int(enabled),
163                "quiet_begin_time": begin_time,
164                "quiet_end_time": end_time,
165            },
166        )

Configure do-not-disturb.

The device expects all three values together via service.set_quiet_time (individual prop.set calls are ignored). begin_time/end_time are minutes since midnight and must be in the range 0-1439 (inclusive).

Ranges that cross midnight are supported by passing a begin_time that is greater than end_time (e.g. 22:00-07:00 is begin_time=1320, end_time=420).

async def set_button_lights(self, enabled: bool) -> None:
168    async def set_button_lights(self, enabled: bool) -> None:
169        """Enable or disable the button/panel lights."""
170        await self.set_prop(RoborockB01Props.LIGHT_MODE, int(enabled))

Enable or disable the button/panel lights.

async def start_clean(self) -> None:
172    async def start_clean(self) -> None:
173        """Start cleaning."""
174        await self.send(
175            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
176            params={
177                "clean_type": CleanTaskTypeMapping.ALL.code,
178                "ctrl_value": SCDeviceCleanParam.START.code,
179                "room_ids": [],
180            },
181        )

Start cleaning.

async def clean_segments(self, segment_ids: list[int]) -> None:
183    async def clean_segments(self, segment_ids: list[int]) -> None:
184        """Start segment cleaning for the given ids (Q7 uses room ids)."""
185        await self.send(
186            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
187            params={
188                "clean_type": CleanTaskTypeMapping.ROOM.code,
189                "ctrl_value": SCDeviceCleanParam.START.code,
190                "room_ids": segment_ids,
191            },
192        )

Start segment cleaning for the given ids (Q7 uses room ids).

async def pause_clean(self) -> None:
194    async def pause_clean(self) -> None:
195        """Pause cleaning."""
196        await self.send(
197            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
198            params={
199                "clean_type": CleanTaskTypeMapping.ALL.code,
200                "ctrl_value": SCDeviceCleanParam.PAUSE.code,
201                "room_ids": [],
202            },
203        )

Pause cleaning.

async def stop_clean(self) -> None:
205    async def stop_clean(self) -> None:
206        """Stop cleaning."""
207        await self.send(
208            command=RoborockB01Q7Methods.SET_ROOM_CLEAN,
209            params={
210                "clean_type": CleanTaskTypeMapping.ALL.code,
211                "ctrl_value": SCDeviceCleanParam.STOP.code,
212                "room_ids": [],
213            },
214        )

Stop cleaning.

async def return_to_dock(self) -> None:
216    async def return_to_dock(self) -> None:
217        """Return to dock."""
218        await self.send(
219            command=RoborockB01Q7Methods.START_RECHARGE,
220            params={},
221        )

Return to dock.

async def find_me(self) -> None:
223    async def find_me(self) -> None:
224        """Locate the robot."""
225        await self.send(
226            command=RoborockB01Q7Methods.FIND_DEVICE,
227            params={},
228        )

Locate the robot.

async def send( self, command: roborock.roborock_typing.RoborockB01Q7Methods | str, params: list | dict | int | None) -> Any:
230    async def send(self, command: CommandType, params: ParamsType) -> Any:
231        """Send a command to the device."""
232        return await self._rpc_channel.send_command(command, params)

Send a command to the device.

class Q10PropertiesApi(roborock.devices.traits.Trait):
 48class Q10PropertiesApi(Trait):
 49    """API for interacting with B01 devices."""
 50
 51    command: CommandTrait
 52    """Trait for sending commands to Q10 devices."""
 53
 54    status: StatusTrait
 55    """Trait for managing the core status of Q10 devices."""
 56
 57    vacuum: VacuumTrait
 58    """Trait for sending vacuum related commands to Q10 devices."""
 59
 60    remote: RemoteTrait
 61    """Trait for sending remote control related commands to Q10 devices."""
 62
 63    volume: SoundVolumeTrait
 64    """Trait for reading / setting the speaker volume."""
 65
 66    child_lock: ChildLockTrait
 67    """Trait for reading / controlling the child lock."""
 68
 69    do_not_disturb: DoNotDisturbTrait
 70    """Trait for reading / controlling Do Not Disturb."""
 71
 72    dust_collection: DustCollectionTrait
 73    """Trait for reading / controlling dock auto-empty (dust collection)."""
 74
 75    button_light: ButtonLightTrait
 76    """Trait for controlling the indicator / button light (LED)."""
 77
 78    network_info: NetworkInfoTrait
 79    """Trait exposing the device's network information."""
 80
 81    consumable: ConsumableTrait
 82    """Trait exposing remaining life of consumables."""
 83
 84    map: MapContentTrait
 85    """Composed map image plus caller-facing map and trace data."""
 86
 87    maps: MapsTrait
 88    """Saved-map list metadata."""
 89
 90    _map_dps: MapDpsTrait
 91    """Private source of restricted zones and virtual walls received through DPS."""
 92
 93    clean_history: CleanHistoryTrait
 94    """Trait for fetching the device clean-record history (``dpCleanRecord``)."""
 95
 96    def __init__(self, channel: B01Q10Channel) -> None:
 97        """Initialize the B01Props API."""
 98        self._channel = channel
 99        self.command = CommandTrait(channel)
100        self.remote = RemoteTrait(self.command)
101        self.status = StatusTrait()
102        self.volume = SoundVolumeTrait(self.command)
103        self.child_lock = ChildLockTrait(self.command)
104        self.do_not_disturb = DoNotDisturbTrait(self.command)
105        self.dust_collection = DustCollectionTrait(self.command)
106        self.button_light = ButtonLightTrait(self.command)
107        self.network_info = NetworkInfoTrait()
108        self.consumable = ConsumableTrait()
109        self._map_dps = MapDpsTrait()
110        self.maps = MapsTrait(self.command)
111        self.map = MapContentTrait(self._map_dps, self.maps, self.command)
112        self.vacuum = VacuumTrait(self.command, self.status, self.map)
113        self.clean_history = CleanHistoryTrait(self.command)
114        # Read-model traits updated from the device's DPS push stream.
115        self._updatable_traits = [
116            self.status,
117            self.volume,
118            self.child_lock,
119            self.do_not_disturb,
120            self.dust_collection,
121            self.network_info,
122            self.consumable,
123            self.clean_history,
124            self._map_dps,
125            self.maps,
126        ]
127        self._subscribe_task: asyncio.Task[None] | None = None
128
129    async def start(self) -> None:
130        """Start any necessary subscriptions for the trait."""
131        self._subscribe_task = asyncio.create_task(self._subscribe_loop())
132
133    async def close(self) -> None:
134        """Close any resources held by the trait."""
135        await self.vacuum.close()
136        if self._subscribe_task is not None:
137            self._subscribe_task.cancel()
138            try:
139                await self._subscribe_task
140            except asyncio.CancelledError:
141                pass  # ignore cancellation errors
142            self._subscribe_task = None
143
144    async def refresh(self) -> None:
145        """Refresh all traits."""
146        # Sending REQUEST_DPS causes the device to publish its ordinary status
147        # values. Map-list and map-content refreshes have separate schedules.
148        await self.command.send(B01_Q10_DP.REQUEST_DPS, params={})
149
150    async def _subscribe_loop(self) -> None:
151        """Persistent loop dispatching decoded messages to the read-model traits."""
152        async for message in self._channel.subscribe_stream():
153            self._handle_message(message)
154
155    def _handle_message(self, message: Q10Message) -> None:
156        """Route a single decoded message to the trait responsible for it.
157
158        Map and trace packets arrive as protocol-301 ``MAP_RESPONSE`` pushes.
159        Map-list DPS responses and other DPS updates feed the read-model traits.
160        """
161        if isinstance(message, Q10MapPacket):
162            self.map.update_from_map_packet(message)
163        elif isinstance(message, Q10TracePacket):
164            self.map.update_from_trace_packet(message)
165        elif isinstance(message, Q10DpsUpdate):
166            _LOGGER.debug("Received Q10 status update: %s", message.dps)
167            # Notify all read-model traits about the new message; each trait
168            # only updates the fields that it is responsible for.
169            for trait in self._updatable_traits:
170                trait.update_from_dps(message.dps)
171
172    def as_dict(self) -> dict[str, Any]:
173        """Return the trait data as a dictionary."""
174        result: dict[str, Any] = {}
175        for name, value in self.__dict__.items():
176            if isinstance(value, RoborockBase) and not name.startswith("_"):
177                result[name] = value.as_dict()
178        if hasattr(self, "map") and hasattr(self.map, "as_dict"):
179            result["map"] = self.map.as_dict()
180        return result

API for interacting with B01 devices.

Q10PropertiesApi(channel: roborock.devices.rpc.b01_q10_channel.B01Q10Channel)
 96    def __init__(self, channel: B01Q10Channel) -> None:
 97        """Initialize the B01Props API."""
 98        self._channel = channel
 99        self.command = CommandTrait(channel)
100        self.remote = RemoteTrait(self.command)
101        self.status = StatusTrait()
102        self.volume = SoundVolumeTrait(self.command)
103        self.child_lock = ChildLockTrait(self.command)
104        self.do_not_disturb = DoNotDisturbTrait(self.command)
105        self.dust_collection = DustCollectionTrait(self.command)
106        self.button_light = ButtonLightTrait(self.command)
107        self.network_info = NetworkInfoTrait()
108        self.consumable = ConsumableTrait()
109        self._map_dps = MapDpsTrait()
110        self.maps = MapsTrait(self.command)
111        self.map = MapContentTrait(self._map_dps, self.maps, self.command)
112        self.vacuum = VacuumTrait(self.command, self.status, self.map)
113        self.clean_history = CleanHistoryTrait(self.command)
114        # Read-model traits updated from the device's DPS push stream.
115        self._updatable_traits = [
116            self.status,
117            self.volume,
118            self.child_lock,
119            self.do_not_disturb,
120            self.dust_collection,
121            self.network_info,
122            self.consumable,
123            self.clean_history,
124            self._map_dps,
125            self.maps,
126        ]
127        self._subscribe_task: asyncio.Task[None] | None = None

Initialize the B01Props API.

command: roborock.devices.traits.b01.q10.command.CommandTrait

Trait for sending commands to Q10 devices.

Trait for managing the core status of Q10 devices.

vacuum: roborock.devices.traits.b01.q10.vacuum.VacuumTrait

Trait for sending vacuum related commands to Q10 devices.

remote: roborock.devices.traits.b01.q10.remote.RemoteTrait

Trait for sending remote control related commands to Q10 devices.

Trait for reading / setting the speaker volume.

Trait for reading / controlling the child lock.

Trait for reading / controlling Do Not Disturb.

Trait for reading / controlling dock auto-empty (dust collection).

Trait for controlling the indicator / button light (LED).

Trait exposing the device's network information.

Trait exposing remaining life of consumables.

Composed map image plus caller-facing map and trace data.

Saved-map list metadata.

Trait for fetching the device clean-record history (dpCleanRecord).

async def start(self) -> None:
129    async def start(self) -> None:
130        """Start any necessary subscriptions for the trait."""
131        self._subscribe_task = asyncio.create_task(self._subscribe_loop())

Start any necessary subscriptions for the trait.

async def close(self) -> None:
133    async def close(self) -> None:
134        """Close any resources held by the trait."""
135        await self.vacuum.close()
136        if self._subscribe_task is not None:
137            self._subscribe_task.cancel()
138            try:
139                await self._subscribe_task
140            except asyncio.CancelledError:
141                pass  # ignore cancellation errors
142            self._subscribe_task = None

Close any resources held by the trait.

async def refresh(self) -> None:
144    async def refresh(self) -> None:
145        """Refresh all traits."""
146        # Sending REQUEST_DPS causes the device to publish its ordinary status
147        # values. Map-list and map-content refreshes have separate schedules.
148        await self.command.send(B01_Q10_DP.REQUEST_DPS, params={})

Refresh all traits.

def as_dict(self) -> dict[str, typing.Any]:
172    def as_dict(self) -> dict[str, Any]:
173        """Return the trait data as a dictionary."""
174        result: dict[str, Any] = {}
175        for name, value in self.__dict__.items():
176            if isinstance(value, RoborockBase) and not name.startswith("_"):
177                result[name] = value.as_dict()
178        if hasattr(self, "map") and hasattr(self.map, "as_dict"):
179            result["map"] = self.map.as_dict()
180        return result

Return the trait data as a dictionary.