roborock.data.v1.v1_containers
1import datetime 2import logging 3import warnings 4from dataclasses import dataclass, field 5from enum import StrEnum 6from typing import Any 7 8from roborock.const import ( 9 CLEANING_BRUSH_REPLACE_TIME, 10 DUST_COLLECTION_REPLACE_TIME, 11 FILTER_REPLACE_TIME, 12 MAIN_BRUSH_REPLACE_TIME, 13 MOP_ROLLER_REPLACE_TIME, 14 NO_MAP, 15 SENSOR_DIRTY_REPLACE_TIME, 16 SIDE_BRUSH_REPLACE_TIME, 17 STRAINER_REPLACE_TIME, 18) 19from roborock.roborock_message import RoborockDataProtocol 20 21from ..containers import NamedRoomMapping, RoborockBase, RoborockBaseTimer, _attr_repr, field_metadata 22from .v1_clean_modes import WashTowelModes 23from .v1_code_mappings import ( 24 CleanFluidStatus, 25 ClearWaterBoxStatus, 26 DirtyWaterBoxStatus, 27 DustBagStatus, 28 RoborockChargeStatus, 29 RoborockCleanType, 30 RoborockDockDustCollectionModeCode, 31 RoborockDockErrorCode, 32 RoborockDockState, 33 RoborockDockTypeCode, 34 RoborockErrorCode, 35 RoborockFinishReason, 36 RoborockInCleaning, 37 RoborockStartType, 38 RoborockStateCode, 39) 40 41_LOGGER = logging.getLogger(__name__) 42 43 44class FieldNameBase(StrEnum): 45 """A base enum class that represents a field name in a RoborockBase dataclass.""" 46 47 48class StatusField(FieldNameBase): 49 """An enum that represents a field in the `StatusV2` class. 50 51 This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait` 52 to understand if a feature is supported by the device using `is_field_supported`. 53 54 The enum values are names of fields in the `StatusV2` class. Each field is 55 annotated with `dps` metadata to map the field to a `RoborockDataProtocol` 56 value used to check support against the product schema. 57 """ 58 59 STATE = "state" 60 BATTERY = "battery" 61 FAN_POWER = "fan_power" 62 WATER_BOX_MODE = "water_box_mode" 63 CHARGE_STATUS = "charge_status" 64 DRY_STATUS = "dry_status" 65 ERROR_CODE = "error_code" 66 WATER_BOX_CARRIAGE_STATUS = "water_box_carriage_status" 67 WATER_BOX_STATUS = "water_box_status" 68 WATER_SHORTAGE_STATUS = "water_shortage_status" 69 DIRTY_WATER_BOX_STATUS = "dirty_water_box_status" 70 CLEAR_WATER_BOX_STATUS = "clear_water_box_status" 71 CLEAN_FLUID_STATUS = "clean_fluid_status" 72 CLEAN_PERCENT = "clean_percent" 73 DOCK_ERROR_STATUS = "dock_error_status" 74 RDT = "rdt" 75 76 77def get_dock_state( 78 state: RoborockStateCode | int | None, 79 battery: int | None, 80 charge_status: RoborockChargeStatus | int | None = None, 81 *, 82 is_supported_valley_electricity: bool = False, 83) -> RoborockDockState: 84 """Return the dock state, accounting for off-peak charging support.""" 85 if state is None or state == RoborockStateCode.unknown: 86 return RoborockDockState.unknown 87 88 # 6. DUSTING 89 if state == RoborockStateCode.emptying_the_bin: 90 return RoborockDockState.dusting 91 92 # 5. FULL 93 if state == RoborockStateCode.charging_complete or (state == RoborockStateCode.charging and battery == 100): 94 return RoborockDockState.full 95 96 # 3 & 4. CHARGING and CHARGE_WAITING 97 if state == RoborockStateCode.charging: 98 if is_supported_valley_electricity and charge_status == RoborockChargeStatus.charge_waiting: 99 return RoborockDockState.off_peak_waiting 100 return RoborockDockState.charging 101 102 # 2. RECHARGING 103 if state in (RoborockStateCode.returning_home, RoborockStateCode.docking): 104 return RoborockDockState.returning 105 106 # 1. IDLE (Not on dock, or doing something else) 107 return RoborockDockState.idle 108 109 110@dataclass 111class StatusV2(RoborockBase): 112 """The result of a GET_STATUS API request.""" 113 114 msg_ver: int | None = None 115 msg_seq: int | None = None 116 state: RoborockStateCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.STATE}) 117 battery: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.BATTERY}) 118 clean_time: int | None = None 119 clean_area: int | None = None 120 error_code: RoborockErrorCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.ERROR_CODE}) 121 map_present: int | None = None 122 in_cleaning: RoborockInCleaning | None = None 123 in_returning: int | None = None 124 in_fresh_state: int | None = None 125 lab_status: int | None = None 126 water_box_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 127 back_type: int | None = None 128 wash_phase: int | None = None 129 wash_ready: int | None = None 130 fan_power: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FAN_POWER}) 131 dnd_enabled: int | None = None 132 map_status: int | None = None 133 is_locating: int | None = None 134 lock_status: int | None = None 135 water_box_mode: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.WATER_BOX_MODE}) 136 water_box_carriage_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 137 mop_forbidden_enable: int | None = None 138 camera_status: int | None = None 139 is_exploring: int | None = None 140 home_sec_status: int | None = None 141 home_sec_enable_password: int | None = None 142 adbumper_status: list[int] | None = None 143 water_shortage_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 144 dock_type: RoborockDockTypeCode | None = None 145 dust_collection_status: int | None = None 146 auto_dust_collection: int | None = None 147 avoid_count: int | None = None 148 mop_mode: int | None = None 149 debug_mode: int | None = None 150 collision_avoid_status: int | None = None 151 switch_map_mode: int | None = None 152 dock_error_status: RoborockDockErrorCode | None = field(default=None, metadata={"dock_feature": "has_dock"}) 153 154 charge_status: RoborockChargeStatus | None = field( 155 default=None, metadata={"dps": RoborockDataProtocol.CHARGE_STATUS} 156 ) 157 unsave_map_reason: int | None = None 158 unsave_map_flag: int | None = None 159 wash_status: int | None = None 160 distance_off: int | None = None 161 in_warmup: int | None = None 162 dry_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.DRYING_STATUS}) 163 rdt: int | None = field(default=None, metadata={"feature": "is_supported_drying"}) 164 clean_percent: int | None = field(default=None, metadata={"feature": "is_support_clean_estimate"}) 165 rss: int | None = None 166 dss: int | None = None 167 common_status: int | None = None 168 corner_clean_mode: int | None = None 169 last_clean_t: int | None = None 170 replenish_mode: int | None = None 171 repeat: int | None = None 172 kct: int | None = None 173 subdivision_sets: int | None = None 174 175 @property 176 def square_meter_clean_area(self) -> float | None: 177 return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None 178 179 @property 180 def error_code_name(self) -> str | None: 181 return self.error_code.display_name if self.error_code is not None else None 182 183 @property 184 def state_name(self) -> str | None: 185 return self.state.display_name if self.state is not None else None 186 187 @property 188 def current_map(self) -> int | None: 189 """Returns the current map ID if the map is present.""" 190 if self.map_status is not None: 191 map_flag = self.map_status >> 2 192 if map_flag != NO_MAP: 193 return map_flag 194 return None 195 196 @property 197 def has_am(self) -> bool | None: 198 if self.dss is None: 199 return None 200 return (self.dss & 3) == 2 201 202 @property 203 @field_metadata(dock_feature="is_washable") 204 def clear_water_box_status(self) -> ClearWaterBoxStatus | None: 205 if self.dss: 206 return ClearWaterBoxStatus((self.dss >> 2) & 3) 207 return None 208 209 @property 210 @field_metadata(dock_feature="is_washable") 211 def dirty_water_box_status(self) -> DirtyWaterBoxStatus | None: 212 if self.dss: 213 return DirtyWaterBoxStatus((self.dss >> 4) & 3) 214 return None 215 216 @property 217 def dust_bag_status(self) -> DustBagStatus | None: 218 if self.dss: 219 return DustBagStatus((self.dss >> 6) & 3) 220 return None 221 222 @property 223 def water_box_filter_status(self) -> int | None: 224 if self.dss: 225 return (self.dss >> 8) & 3 226 return None 227 228 @property 229 @field_metadata( 230 dock_feature="is_clean_fluid_auto_delivery_supported", 231 ) 232 def clean_fluid_status(self) -> CleanFluidStatus | None: 233 if self.dss: 234 value = (self.dss >> 10) & 3 235 if value == 0: 236 return None # Feature not supported by this device 237 return CleanFluidStatus(value) 238 return None 239 240 @property 241 def hatch_door_status(self) -> int | None: 242 if self.dss: 243 return (self.dss >> 12) & 7 244 return None 245 246 @property 247 def dock_cool_fan_status(self) -> int | None: 248 if self.dss: 249 return (self.dss >> 15) & 3 250 return None 251 252 @property 253 def dock_state(self) -> RoborockDockState: 254 """A synthesized, high-level dock state reflecting the UI's display. 255 256 .. deprecated:: 257 Use StatusTrait.dock_state or get_dock_state instead. 258 """ 259 warnings.warn( 260 "StatusV2.dock_state is deprecated and will be removed in a future release. " 261 "Use StatusTrait.dock_state or get_dock_state instead.", 262 DeprecationWarning, 263 stacklevel=2, 264 ) 265 return get_dock_state( 266 state=self.state, 267 battery=self.battery, 268 charge_status=self.charge_status, 269 is_supported_valley_electricity=True, 270 ) 271 272 @property 273 def is_battery_charging(self) -> bool: 274 """Return whether the battery is actively charging.""" 275 return self.dock_state == RoborockDockState.charging 276 277 def __repr__(self) -> str: 278 return _attr_repr(self) 279 280 281@dataclass 282class DnDTimer(RoborockBaseTimer): 283 """DnDTimer""" 284 285 286@dataclass 287class ValleyElectricityTimer(RoborockBaseTimer): 288 """ValleyElectricityTimer""" 289 290 291@dataclass 292class CleanSummary(RoborockBase): 293 clean_time: int | None = None 294 clean_area: int | None = None 295 clean_count: int | None = None 296 dust_collection_count: int | None = None 297 records: list[int] | None = None 298 last_clean_t: int | None = None 299 300 @property 301 def square_meter_clean_area(self) -> float | None: 302 """Returns the clean area in square meters.""" 303 if isinstance(self.clean_area, list | str): 304 _LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}") 305 return None 306 return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None 307 308 def __repr__(self) -> str: 309 """Return a string representation of the object including all attributes.""" 310 return _attr_repr(self) 311 312 313@dataclass 314class CleanRecord(RoborockBase): 315 begin: int | None = None 316 end: int | None = None 317 duration: int | None = None 318 area: int | None = None 319 error: int | None = None 320 complete: int | None = None 321 start_type: RoborockStartType | None = None 322 clean_type: RoborockCleanType | None = None 323 finish_reason: RoborockFinishReason | None = None 324 dust_collection_status: int | None = None 325 avoid_count: int | None = None 326 wash_count: int | None = None 327 map_flag: int | None = None 328 329 @property 330 def square_meter_area(self) -> float | None: 331 return round(self.area / 1000000, 1) if self.area is not None else None 332 333 @property 334 def begin_datetime(self) -> datetime.datetime | None: 335 return datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None 336 337 @property 338 def end_datetime(self) -> datetime.datetime | None: 339 return datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None 340 341 def __repr__(self) -> str: 342 return _attr_repr(self) 343 344 345class CleanSummaryWithDetail(CleanSummary): 346 """CleanSummary with the last CleanRecord included.""" 347 348 last_clean_record: CleanRecord | None = None 349 350 351class ConsumableField(FieldNameBase): 352 """An enum that represents a field in the `Consumable` class. 353 354 This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait` 355 to understand if a feature is supported by the device using `is_field_supported`. 356 357 The enum values are names of fields in the `Consumable` class. Each field is 358 annotated with `dps` metadata to map the field to a `RoborockDataProtocol` 359 value used to check support against the product schema. 360 """ 361 362 MAIN_BRUSH_WORK_TIME = "main_brush_work_time" 363 SIDE_BRUSH_WORK_TIME = "side_brush_work_time" 364 FILTER_WORK_TIME = "filter_work_time" 365 366 367@dataclass 368class Consumable(RoborockBase): 369 main_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.MAIN_BRUSH_WORK_TIME}) 370 side_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.SIDE_BRUSH_WORK_TIME}) 371 filter_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FILTER_WORK_TIME}) 372 filter_element_work_time: int | None = None 373 sensor_dirty_time: int | None = None 374 strainer_work_times: int | None = None 375 dust_collection_work_times: int | None = None 376 cleaning_brush_work_times: int | None = None 377 moproller_work_time: int | None = None 378 379 @property 380 def main_brush_time_left(self) -> int | None: 381 return MAIN_BRUSH_REPLACE_TIME - self.main_brush_work_time if self.main_brush_work_time is not None else None 382 383 @property 384 def side_brush_time_left(self) -> int | None: 385 return SIDE_BRUSH_REPLACE_TIME - self.side_brush_work_time if self.side_brush_work_time is not None else None 386 387 @property 388 def filter_time_left(self) -> int | None: 389 return FILTER_REPLACE_TIME - self.filter_work_time if self.filter_work_time is not None else None 390 391 @property 392 def sensor_time_left(self) -> int | None: 393 return SENSOR_DIRTY_REPLACE_TIME - self.sensor_dirty_time if self.sensor_dirty_time is not None else None 394 395 @property 396 def strainer_time_left(self) -> int | None: 397 return STRAINER_REPLACE_TIME - self.strainer_work_times if self.strainer_work_times is not None else None 398 399 @property 400 def dust_collection_time_left(self) -> int | None: 401 return ( 402 DUST_COLLECTION_REPLACE_TIME - self.dust_collection_work_times 403 if self.dust_collection_work_times is not None 404 else None 405 ) 406 407 @property 408 def cleaning_brush_time_left(self) -> int | None: 409 return ( 410 CLEANING_BRUSH_REPLACE_TIME - self.cleaning_brush_work_times 411 if self.cleaning_brush_work_times is not None 412 else None 413 ) 414 415 @property 416 def mop_roller_time_left(self) -> int | None: 417 return MOP_ROLLER_REPLACE_TIME - self.moproller_work_time if self.moproller_work_time is not None else None 418 419 def __repr__(self) -> str: 420 return _attr_repr(self) 421 422 423@dataclass 424class MultiMapsListRoom(RoborockBase): 425 id: int | None = None 426 tag: int | None = None 427 iot_name_id: str | None = None 428 iot_name: str | None = None 429 430 @property 431 def named_room_mapping(self) -> NamedRoomMapping | None: 432 """Returns a NamedRoomMapping object if valid.""" 433 if self.id is None or self.iot_name_id is None: 434 return None 435 return NamedRoomMapping( 436 segment_id=self.id, 437 iot_id=self.iot_name_id, 438 raw_name=self.iot_name, 439 ) 440 441 442@dataclass 443class MultiMapsListMapInfoBakMaps(RoborockBase): 444 mapflag: Any | None = None 445 add_time: Any | None = None 446 447 448@dataclass 449class MultiMapsListMapInfo(RoborockBase): 450 map_flag: int 451 name: str 452 add_time: Any | None = None 453 length: Any | None = None 454 bak_maps: list[MultiMapsListMapInfoBakMaps] | None = None 455 rooms: list[MultiMapsListRoom] | None = None 456 457 @property 458 def mapFlag(self) -> int: 459 """Alias for map_flag, returns the map flag as an integer.""" 460 return self.map_flag 461 462 @property 463 def rooms_map(self) -> dict[int, NamedRoomMapping]: 464 """Returns a dictionary of room mappings by segment id.""" 465 return { 466 room.id: mapping 467 for room in self.rooms or () 468 if room.id is not None and (mapping := room.named_room_mapping) is not None 469 } 470 471 472@dataclass 473class MultiMapsList(RoborockBase): 474 max_multi_map: int | None = None 475 max_bak_map: int | None = None 476 multi_map_count: int | None = None 477 map_info: list[MultiMapsListMapInfo] | None = None 478 479 480@dataclass 481class SmartWashParams(RoborockBase): 482 smart_wash: int | None = None 483 wash_interval: int | None = None 484 485 486@dataclass 487class MopDryerProfile(RoborockBase): 488 """Dryer parameters for one state of the auto mop-drying setting.""" 489 490 cliff_on: int | None = None 491 cliff_off: int | None = None 492 count: int | None = None 493 dry_time: int | None = None 494 """Drying duration in seconds. Only present in the ``on`` profile.""" 495 dry_heating_film_time: int | None = None 496 """Heating element run time in seconds. Only present in the ``on`` profile.""" 497 498 499@dataclass 500class MopDryerSetting(RoborockBase): 501 """Auto mop-drying setting, as returned by APP_GET_DRYER_SETTING.""" 502 503 status: int | None = None 504 on: MopDryerProfile | None = None 505 off: MopDryerProfile | None = None 506 507 508@dataclass 509class DustCollectionMode(RoborockBase): 510 mode: RoborockDockDustCollectionModeCode | None = None 511 512 513@dataclass 514class WashTowelMode(RoborockBase): 515 wash_mode: WashTowelModes | None = None 516 517 518SEQ_TYPE_CLEAN_THEN_MOP = 1 519 520 521@dataclass 522class SegmentCleanMode(RoborockBase): 523 """A single room's clean mode override, as reported by the device. 524 525 `fan_power` / `water_box_mode` / `mop_mode` / `seq_type` are kept as raw 526 `int | None` wire codes rather than typed enums, matching `StatusV2`, so 527 model-specific values (e.g. water-slide codes like 222) round-trip 528 losslessly even for codes this library doesn't otherwise model. 529 530 `mop_power` and `custom_water_box_distance` are only reported by some 531 models (roller-mop / custom-water-flow devices); they are `None` on 532 models that don't report them. 533 """ 534 535 segment: int 536 fan_power: int | None = None 537 water_box_mode: int | None = None 538 mop_mode: int | None = None 539 repeat: int | None = None 540 seq_type: int | None = None 541 mop_power: int | None = None 542 custom_water_box_distance: int | None = None 543 544 @property 545 def clean_then_mop(self) -> bool | None: 546 """Whether this room is vacuumed fully before it is mopped. 547 548 `None` means the device did not report a sequence type for this 549 room's override. 550 """ 551 if self.seq_type is None: 552 return None 553 return self.seq_type == SEQ_TYPE_CLEAN_THEN_MOP 554 555 556@dataclass 557class CustomizeCleanModes(RoborockBase): 558 """Collection of per-room clean mode overrides.""" 559 560 modes: list[SegmentCleanMode] | None = None 561 562 @property 563 def by_segment(self) -> dict[int, SegmentCleanMode]: 564 """Index the overrides by segment id.""" 565 return {mode.segment: mode for mode in self.modes or []} 566 567 568@dataclass 569class NetworkInfo(RoborockBase): 570 ip: str 571 ssid: str | None = None 572 mac: str | None = None 573 bssid: str | None = None 574 rssi: int | None = None 575 576 577@dataclass 578class AppInitStatusLocalInfo(RoborockBase): 579 location: str 580 bom: str | None = None 581 featureset: int | None = None 582 language: str | None = None 583 logserver: str | None = None 584 wifiplan: str | None = None 585 timezone: str | None = None 586 name: str | None = None 587 588 589@dataclass 590class AppInitStatus(RoborockBase): 591 local_info: AppInitStatusLocalInfo 592 feature_info: list[int] 593 new_feature_info: int = 0 594 new_feature_info_str: str = "" 595 new_feature_info_2: int | None = None 596 carriage_type: int | None = None 597 dsp_version: str | None = None 598 599 600@dataclass 601class ChildLockStatus(RoborockBase): 602 lock_status: int = 0 603 604 605@dataclass 606class FlowLedStatus(RoborockBase): 607 status: int = 0 608 609 610@dataclass 611class LedStatus(RoborockBase): 612 status: int = 0
45class FieldNameBase(StrEnum): 46 """A base enum class that represents a field name in a RoborockBase dataclass."""
A base enum class that represents a field name in a RoborockBase dataclass.
49class StatusField(FieldNameBase): 50 """An enum that represents a field in the `StatusV2` class. 51 52 This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait` 53 to understand if a feature is supported by the device using `is_field_supported`. 54 55 The enum values are names of fields in the `StatusV2` class. Each field is 56 annotated with `dps` metadata to map the field to a `RoborockDataProtocol` 57 value used to check support against the product schema. 58 """ 59 60 STATE = "state" 61 BATTERY = "battery" 62 FAN_POWER = "fan_power" 63 WATER_BOX_MODE = "water_box_mode" 64 CHARGE_STATUS = "charge_status" 65 DRY_STATUS = "dry_status" 66 ERROR_CODE = "error_code" 67 WATER_BOX_CARRIAGE_STATUS = "water_box_carriage_status" 68 WATER_BOX_STATUS = "water_box_status" 69 WATER_SHORTAGE_STATUS = "water_shortage_status" 70 DIRTY_WATER_BOX_STATUS = "dirty_water_box_status" 71 CLEAR_WATER_BOX_STATUS = "clear_water_box_status" 72 CLEAN_FLUID_STATUS = "clean_fluid_status" 73 CLEAN_PERCENT = "clean_percent" 74 DOCK_ERROR_STATUS = "dock_error_status" 75 RDT = "rdt"
An enum that represents a field in the StatusV2 class.
This is used with roborock.devices.traits.v1.status.DeviceFeaturesTrait
to understand if a feature is supported by the device using is_field_supported.
The enum values are names of fields in the StatusV2 class. Each field is
annotated with dps metadata to map the field to a RoborockDataProtocol
value used to check support against the product schema.
78def get_dock_state( 79 state: RoborockStateCode | int | None, 80 battery: int | None, 81 charge_status: RoborockChargeStatus | int | None = None, 82 *, 83 is_supported_valley_electricity: bool = False, 84) -> RoborockDockState: 85 """Return the dock state, accounting for off-peak charging support.""" 86 if state is None or state == RoborockStateCode.unknown: 87 return RoborockDockState.unknown 88 89 # 6. DUSTING 90 if state == RoborockStateCode.emptying_the_bin: 91 return RoborockDockState.dusting 92 93 # 5. FULL 94 if state == RoborockStateCode.charging_complete or (state == RoborockStateCode.charging and battery == 100): 95 return RoborockDockState.full 96 97 # 3 & 4. CHARGING and CHARGE_WAITING 98 if state == RoborockStateCode.charging: 99 if is_supported_valley_electricity and charge_status == RoborockChargeStatus.charge_waiting: 100 return RoborockDockState.off_peak_waiting 101 return RoborockDockState.charging 102 103 # 2. RECHARGING 104 if state in (RoborockStateCode.returning_home, RoborockStateCode.docking): 105 return RoborockDockState.returning 106 107 # 1. IDLE (Not on dock, or doing something else) 108 return RoborockDockState.idle
Return the dock state, accounting for off-peak charging support.
111@dataclass 112class StatusV2(RoborockBase): 113 """The result of a GET_STATUS API request.""" 114 115 msg_ver: int | None = None 116 msg_seq: int | None = None 117 state: RoborockStateCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.STATE}) 118 battery: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.BATTERY}) 119 clean_time: int | None = None 120 clean_area: int | None = None 121 error_code: RoborockErrorCode | None = field(default=None, metadata={"dps": RoborockDataProtocol.ERROR_CODE}) 122 map_present: int | None = None 123 in_cleaning: RoborockInCleaning | None = None 124 in_returning: int | None = None 125 in_fresh_state: int | None = None 126 lab_status: int | None = None 127 water_box_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 128 back_type: int | None = None 129 wash_phase: int | None = None 130 wash_ready: int | None = None 131 fan_power: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FAN_POWER}) 132 dnd_enabled: int | None = None 133 map_status: int | None = None 134 is_locating: int | None = None 135 lock_status: int | None = None 136 water_box_mode: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.WATER_BOX_MODE}) 137 water_box_carriage_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 138 mop_forbidden_enable: int | None = None 139 camera_status: int | None = None 140 is_exploring: int | None = None 141 home_sec_status: int | None = None 142 home_sec_enable_password: int | None = None 143 adbumper_status: list[int] | None = None 144 water_shortage_status: int | None = field(default=None, metadata={"feature": "is_support_water_mode"}) 145 dock_type: RoborockDockTypeCode | None = None 146 dust_collection_status: int | None = None 147 auto_dust_collection: int | None = None 148 avoid_count: int | None = None 149 mop_mode: int | None = None 150 debug_mode: int | None = None 151 collision_avoid_status: int | None = None 152 switch_map_mode: int | None = None 153 dock_error_status: RoborockDockErrorCode | None = field(default=None, metadata={"dock_feature": "has_dock"}) 154 155 charge_status: RoborockChargeStatus | None = field( 156 default=None, metadata={"dps": RoborockDataProtocol.CHARGE_STATUS} 157 ) 158 unsave_map_reason: int | None = None 159 unsave_map_flag: int | None = None 160 wash_status: int | None = None 161 distance_off: int | None = None 162 in_warmup: int | None = None 163 dry_status: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.DRYING_STATUS}) 164 rdt: int | None = field(default=None, metadata={"feature": "is_supported_drying"}) 165 clean_percent: int | None = field(default=None, metadata={"feature": "is_support_clean_estimate"}) 166 rss: int | None = None 167 dss: int | None = None 168 common_status: int | None = None 169 corner_clean_mode: int | None = None 170 last_clean_t: int | None = None 171 replenish_mode: int | None = None 172 repeat: int | None = None 173 kct: int | None = None 174 subdivision_sets: int | None = None 175 176 @property 177 def square_meter_clean_area(self) -> float | None: 178 return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None 179 180 @property 181 def error_code_name(self) -> str | None: 182 return self.error_code.display_name if self.error_code is not None else None 183 184 @property 185 def state_name(self) -> str | None: 186 return self.state.display_name if self.state is not None else None 187 188 @property 189 def current_map(self) -> int | None: 190 """Returns the current map ID if the map is present.""" 191 if self.map_status is not None: 192 map_flag = self.map_status >> 2 193 if map_flag != NO_MAP: 194 return map_flag 195 return None 196 197 @property 198 def has_am(self) -> bool | None: 199 if self.dss is None: 200 return None 201 return (self.dss & 3) == 2 202 203 @property 204 @field_metadata(dock_feature="is_washable") 205 def clear_water_box_status(self) -> ClearWaterBoxStatus | None: 206 if self.dss: 207 return ClearWaterBoxStatus((self.dss >> 2) & 3) 208 return None 209 210 @property 211 @field_metadata(dock_feature="is_washable") 212 def dirty_water_box_status(self) -> DirtyWaterBoxStatus | None: 213 if self.dss: 214 return DirtyWaterBoxStatus((self.dss >> 4) & 3) 215 return None 216 217 @property 218 def dust_bag_status(self) -> DustBagStatus | None: 219 if self.dss: 220 return DustBagStatus((self.dss >> 6) & 3) 221 return None 222 223 @property 224 def water_box_filter_status(self) -> int | None: 225 if self.dss: 226 return (self.dss >> 8) & 3 227 return None 228 229 @property 230 @field_metadata( 231 dock_feature="is_clean_fluid_auto_delivery_supported", 232 ) 233 def clean_fluid_status(self) -> CleanFluidStatus | None: 234 if self.dss: 235 value = (self.dss >> 10) & 3 236 if value == 0: 237 return None # Feature not supported by this device 238 return CleanFluidStatus(value) 239 return None 240 241 @property 242 def hatch_door_status(self) -> int | None: 243 if self.dss: 244 return (self.dss >> 12) & 7 245 return None 246 247 @property 248 def dock_cool_fan_status(self) -> int | None: 249 if self.dss: 250 return (self.dss >> 15) & 3 251 return None 252 253 @property 254 def dock_state(self) -> RoborockDockState: 255 """A synthesized, high-level dock state reflecting the UI's display. 256 257 .. deprecated:: 258 Use StatusTrait.dock_state or get_dock_state instead. 259 """ 260 warnings.warn( 261 "StatusV2.dock_state is deprecated and will be removed in a future release. " 262 "Use StatusTrait.dock_state or get_dock_state instead.", 263 DeprecationWarning, 264 stacklevel=2, 265 ) 266 return get_dock_state( 267 state=self.state, 268 battery=self.battery, 269 charge_status=self.charge_status, 270 is_supported_valley_electricity=True, 271 ) 272 273 @property 274 def is_battery_charging(self) -> bool: 275 """Return whether the battery is actively charging.""" 276 return self.dock_state == RoborockDockState.charging 277 278 def __repr__(self) -> str: 279 return _attr_repr(self)
The result of a GET_STATUS API request.
188 @property 189 def current_map(self) -> int | None: 190 """Returns the current map ID if the map is present.""" 191 if self.map_status is not None: 192 map_flag = self.map_status >> 2 193 if map_flag != NO_MAP: 194 return map_flag 195 return None
Returns the current map ID if the map is present.
229 @property 230 @field_metadata( 231 dock_feature="is_clean_fluid_auto_delivery_supported", 232 ) 233 def clean_fluid_status(self) -> CleanFluidStatus | None: 234 if self.dss: 235 value = (self.dss >> 10) & 3 236 if value == 0: 237 return None # Feature not supported by this device 238 return CleanFluidStatus(value) 239 return None
253 @property 254 def dock_state(self) -> RoborockDockState: 255 """A synthesized, high-level dock state reflecting the UI's display. 256 257 .. deprecated:: 258 Use StatusTrait.dock_state or get_dock_state instead. 259 """ 260 warnings.warn( 261 "StatusV2.dock_state is deprecated and will be removed in a future release. " 262 "Use StatusTrait.dock_state or get_dock_state instead.", 263 DeprecationWarning, 264 stacklevel=2, 265 ) 266 return get_dock_state( 267 state=self.state, 268 battery=self.battery, 269 charge_status=self.charge_status, 270 is_supported_valley_electricity=True, 271 )
A synthesized, high-level dock state reflecting the UI's display.
Deprecated since version : Use StatusTrait.dock_state or get_dock_state instead.
273 @property 274 def is_battery_charging(self) -> bool: 275 """Return whether the battery is actively charging.""" 276 return self.dock_state == RoborockDockState.charging
Return whether the battery is actively charging.
Inherited Members
DnDTimer
ValleyElectricityTimer
292@dataclass 293class CleanSummary(RoborockBase): 294 clean_time: int | None = None 295 clean_area: int | None = None 296 clean_count: int | None = None 297 dust_collection_count: int | None = None 298 records: list[int] | None = None 299 last_clean_t: int | None = None 300 301 @property 302 def square_meter_clean_area(self) -> float | None: 303 """Returns the clean area in square meters.""" 304 if isinstance(self.clean_area, list | str): 305 _LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}") 306 return None 307 return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None 308 309 def __repr__(self) -> str: 310 """Return a string representation of the object including all attributes.""" 311 return _attr_repr(self)
301 @property 302 def square_meter_clean_area(self) -> float | None: 303 """Returns the clean area in square meters.""" 304 if isinstance(self.clean_area, list | str): 305 _LOGGER.warning(f"Clean area is a unexpected type! Please give the following in a issue: {self.clean_area}") 306 return None 307 return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
Returns the clean area in square meters.
Inherited Members
314@dataclass 315class CleanRecord(RoborockBase): 316 begin: int | None = None 317 end: int | None = None 318 duration: int | None = None 319 area: int | None = None 320 error: int | None = None 321 complete: int | None = None 322 start_type: RoborockStartType | None = None 323 clean_type: RoborockCleanType | None = None 324 finish_reason: RoborockFinishReason | None = None 325 dust_collection_status: int | None = None 326 avoid_count: int | None = None 327 wash_count: int | None = None 328 map_flag: int | None = None 329 330 @property 331 def square_meter_area(self) -> float | None: 332 return round(self.area / 1000000, 1) if self.area is not None else None 333 334 @property 335 def begin_datetime(self) -> datetime.datetime | None: 336 return datetime.datetime.fromtimestamp(self.begin).astimezone(datetime.UTC) if self.begin else None 337 338 @property 339 def end_datetime(self) -> datetime.datetime | None: 340 return datetime.datetime.fromtimestamp(self.end).astimezone(datetime.UTC) if self.end else None 341 342 def __repr__(self) -> str: 343 return _attr_repr(self)
Inherited Members
346class CleanSummaryWithDetail(CleanSummary): 347 """CleanSummary with the last CleanRecord included.""" 348 349 last_clean_record: CleanRecord | None = None
CleanSummary with the last CleanRecord included.
352class ConsumableField(FieldNameBase): 353 """An enum that represents a field in the `Consumable` class. 354 355 This is used with `roborock.devices.traits.v1.status.DeviceFeaturesTrait` 356 to understand if a feature is supported by the device using `is_field_supported`. 357 358 The enum values are names of fields in the `Consumable` class. Each field is 359 annotated with `dps` metadata to map the field to a `RoborockDataProtocol` 360 value used to check support against the product schema. 361 """ 362 363 MAIN_BRUSH_WORK_TIME = "main_brush_work_time" 364 SIDE_BRUSH_WORK_TIME = "side_brush_work_time" 365 FILTER_WORK_TIME = "filter_work_time"
An enum that represents a field in the Consumable class.
This is used with roborock.devices.traits.v1.status.DeviceFeaturesTrait
to understand if a feature is supported by the device using is_field_supported.
The enum values are names of fields in the Consumable class. Each field is
annotated with dps metadata to map the field to a RoborockDataProtocol
value used to check support against the product schema.
368@dataclass 369class Consumable(RoborockBase): 370 main_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.MAIN_BRUSH_WORK_TIME}) 371 side_brush_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.SIDE_BRUSH_WORK_TIME}) 372 filter_work_time: int | None = field(default=None, metadata={"dps": RoborockDataProtocol.FILTER_WORK_TIME}) 373 filter_element_work_time: int | None = None 374 sensor_dirty_time: int | None = None 375 strainer_work_times: int | None = None 376 dust_collection_work_times: int | None = None 377 cleaning_brush_work_times: int | None = None 378 moproller_work_time: int | None = None 379 380 @property 381 def main_brush_time_left(self) -> int | None: 382 return MAIN_BRUSH_REPLACE_TIME - self.main_brush_work_time if self.main_brush_work_time is not None else None 383 384 @property 385 def side_brush_time_left(self) -> int | None: 386 return SIDE_BRUSH_REPLACE_TIME - self.side_brush_work_time if self.side_brush_work_time is not None else None 387 388 @property 389 def filter_time_left(self) -> int | None: 390 return FILTER_REPLACE_TIME - self.filter_work_time if self.filter_work_time is not None else None 391 392 @property 393 def sensor_time_left(self) -> int | None: 394 return SENSOR_DIRTY_REPLACE_TIME - self.sensor_dirty_time if self.sensor_dirty_time is not None else None 395 396 @property 397 def strainer_time_left(self) -> int | None: 398 return STRAINER_REPLACE_TIME - self.strainer_work_times if self.strainer_work_times is not None else None 399 400 @property 401 def dust_collection_time_left(self) -> int | None: 402 return ( 403 DUST_COLLECTION_REPLACE_TIME - self.dust_collection_work_times 404 if self.dust_collection_work_times is not None 405 else None 406 ) 407 408 @property 409 def cleaning_brush_time_left(self) -> int | None: 410 return ( 411 CLEANING_BRUSH_REPLACE_TIME - self.cleaning_brush_work_times 412 if self.cleaning_brush_work_times is not None 413 else None 414 ) 415 416 @property 417 def mop_roller_time_left(self) -> int | None: 418 return MOP_ROLLER_REPLACE_TIME - self.moproller_work_time if self.moproller_work_time is not None else None 419 420 def __repr__(self) -> str: 421 return _attr_repr(self)
Inherited Members
424@dataclass 425class MultiMapsListRoom(RoborockBase): 426 id: int | None = None 427 tag: int | None = None 428 iot_name_id: str | None = None 429 iot_name: str | None = None 430 431 @property 432 def named_room_mapping(self) -> NamedRoomMapping | None: 433 """Returns a NamedRoomMapping object if valid.""" 434 if self.id is None or self.iot_name_id is None: 435 return None 436 return NamedRoomMapping( 437 segment_id=self.id, 438 iot_id=self.iot_name_id, 439 raw_name=self.iot_name, 440 )
431 @property 432 def named_room_mapping(self) -> NamedRoomMapping | None: 433 """Returns a NamedRoomMapping object if valid.""" 434 if self.id is None or self.iot_name_id is None: 435 return None 436 return NamedRoomMapping( 437 segment_id=self.id, 438 iot_id=self.iot_name_id, 439 raw_name=self.iot_name, 440 )
Returns a NamedRoomMapping object if valid.
Inherited Members
443@dataclass 444class MultiMapsListMapInfoBakMaps(RoborockBase): 445 mapflag: Any | None = None 446 add_time: Any | None = None
Inherited Members
449@dataclass 450class MultiMapsListMapInfo(RoborockBase): 451 map_flag: int 452 name: str 453 add_time: Any | None = None 454 length: Any | None = None 455 bak_maps: list[MultiMapsListMapInfoBakMaps] | None = None 456 rooms: list[MultiMapsListRoom] | None = None 457 458 @property 459 def mapFlag(self) -> int: 460 """Alias for map_flag, returns the map flag as an integer.""" 461 return self.map_flag 462 463 @property 464 def rooms_map(self) -> dict[int, NamedRoomMapping]: 465 """Returns a dictionary of room mappings by segment id.""" 466 return { 467 room.id: mapping 468 for room in self.rooms or () 469 if room.id is not None and (mapping := room.named_room_mapping) is not None 470 }
458 @property 459 def mapFlag(self) -> int: 460 """Alias for map_flag, returns the map flag as an integer.""" 461 return self.map_flag
Alias for map_flag, returns the map flag as an integer.
463 @property 464 def rooms_map(self) -> dict[int, NamedRoomMapping]: 465 """Returns a dictionary of room mappings by segment id.""" 466 return { 467 room.id: mapping 468 for room in self.rooms or () 469 if room.id is not None and (mapping := room.named_room_mapping) is not None 470 }
Returns a dictionary of room mappings by segment id.
Inherited Members
473@dataclass 474class MultiMapsList(RoborockBase): 475 max_multi_map: int | None = None 476 max_bak_map: int | None = None 477 multi_map_count: int | None = None 478 map_info: list[MultiMapsListMapInfo] | None = None
Inherited Members
481@dataclass 482class SmartWashParams(RoborockBase): 483 smart_wash: int | None = None 484 wash_interval: int | None = None
Inherited Members
487@dataclass 488class MopDryerProfile(RoborockBase): 489 """Dryer parameters for one state of the auto mop-drying setting.""" 490 491 cliff_on: int | None = None 492 cliff_off: int | None = None 493 count: int | None = None 494 dry_time: int | None = None 495 """Drying duration in seconds. Only present in the ``on`` profile.""" 496 dry_heating_film_time: int | None = None 497 """Heating element run time in seconds. Only present in the ``on`` profile."""
Dryer parameters for one state of the auto mop-drying setting.
Heating element run time in seconds. Only present in the on profile.
Inherited Members
500@dataclass 501class MopDryerSetting(RoborockBase): 502 """Auto mop-drying setting, as returned by APP_GET_DRYER_SETTING.""" 503 504 status: int | None = None 505 on: MopDryerProfile | None = None 506 off: MopDryerProfile | None = None
Auto mop-drying setting, as returned by APP_GET_DRYER_SETTING.
Inherited Members
509@dataclass 510class DustCollectionMode(RoborockBase): 511 mode: RoborockDockDustCollectionModeCode | None = None
Inherited Members
Inherited Members
522@dataclass 523class SegmentCleanMode(RoborockBase): 524 """A single room's clean mode override, as reported by the device. 525 526 `fan_power` / `water_box_mode` / `mop_mode` / `seq_type` are kept as raw 527 `int | None` wire codes rather than typed enums, matching `StatusV2`, so 528 model-specific values (e.g. water-slide codes like 222) round-trip 529 losslessly even for codes this library doesn't otherwise model. 530 531 `mop_power` and `custom_water_box_distance` are only reported by some 532 models (roller-mop / custom-water-flow devices); they are `None` on 533 models that don't report them. 534 """ 535 536 segment: int 537 fan_power: int | None = None 538 water_box_mode: int | None = None 539 mop_mode: int | None = None 540 repeat: int | None = None 541 seq_type: int | None = None 542 mop_power: int | None = None 543 custom_water_box_distance: int | None = None 544 545 @property 546 def clean_then_mop(self) -> bool | None: 547 """Whether this room is vacuumed fully before it is mopped. 548 549 `None` means the device did not report a sequence type for this 550 room's override. 551 """ 552 if self.seq_type is None: 553 return None 554 return self.seq_type == SEQ_TYPE_CLEAN_THEN_MOP
A single room's clean mode override, as reported by the device.
fan_power / water_box_mode / mop_mode / seq_type are kept as raw
int | None wire codes rather than typed enums, matching StatusV2, so
model-specific values (e.g. water-slide codes like 222) round-trip
losslessly even for codes this library doesn't otherwise model.
mop_power and custom_water_box_distance are only reported by some
models (roller-mop / custom-water-flow devices); they are None on
models that don't report them.
545 @property 546 def clean_then_mop(self) -> bool | None: 547 """Whether this room is vacuumed fully before it is mopped. 548 549 `None` means the device did not report a sequence type for this 550 room's override. 551 """ 552 if self.seq_type is None: 553 return None 554 return self.seq_type == SEQ_TYPE_CLEAN_THEN_MOP
Whether this room is vacuumed fully before it is mopped.
None means the device did not report a sequence type for this
room's override.
Inherited Members
557@dataclass 558class CustomizeCleanModes(RoborockBase): 559 """Collection of per-room clean mode overrides.""" 560 561 modes: list[SegmentCleanMode] | None = None 562 563 @property 564 def by_segment(self) -> dict[int, SegmentCleanMode]: 565 """Index the overrides by segment id.""" 566 return {mode.segment: mode for mode in self.modes or []}
Collection of per-room clean mode overrides.
563 @property 564 def by_segment(self) -> dict[int, SegmentCleanMode]: 565 """Index the overrides by segment id.""" 566 return {mode.segment: mode for mode in self.modes or []}
Index the overrides by segment id.
Inherited Members
569@dataclass 570class NetworkInfo(RoborockBase): 571 ip: str 572 ssid: str | None = None 573 mac: str | None = None 574 bssid: str | None = None 575 rssi: int | None = None
Inherited Members
578@dataclass 579class AppInitStatusLocalInfo(RoborockBase): 580 location: str 581 bom: str | None = None 582 featureset: int | None = None 583 language: str | None = None 584 logserver: str | None = None 585 wifiplan: str | None = None 586 timezone: str | None = None 587 name: str | None = None
Inherited Members
590@dataclass 591class AppInitStatus(RoborockBase): 592 local_info: AppInitStatusLocalInfo 593 feature_info: list[int] 594 new_feature_info: int = 0 595 new_feature_info_str: str = "" 596 new_feature_info_2: int | None = None 597 carriage_type: int | None = None 598 dsp_version: str | None = None