1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
|
import datetime
import json
from dataclasses import dataclass, field
from functools import cached_property
from ...exceptions import RoborockException
from ..containers import RoborockBase
from .b01_q7_code_mappings import (
B01Fault,
CleanPathPreferenceMapping,
CleanRepeatMapping,
CleanTypeMapping,
SCWindMapping,
WaterLevelMapping,
WorkModeMapping,
WorkStatusMapping,
)
@dataclass
class NetStatus(RoborockBase):
"""Represents the network status of the device."""
rssi: str
loss: int
ping: int
ip: str
mac: str
ssid: str
frequency: int
bssid: str
@dataclass
class OrderTotal(RoborockBase):
"""Represents the order total information."""
total: int
enable: int
@dataclass
class Privacy(RoborockBase):
"""Represents the privacy settings of the device."""
ai_recognize: int
dirt_recognize: int
pet_recognize: int
carpet_turbo: int
carpet_avoid: int
carpet_show: int
map_uploads: int
ai_agent: int
ai_avoidance: int
record_uploads: int
along_floor: int
auto_upgrade: int
@dataclass
class PvCharging(RoborockBase):
"""Represents the photovoltaic charging status."""
status: int
begin_time: int
end_time: int
@dataclass
class Recommend(RoborockBase):
"""Represents cleaning recommendations."""
sill: int
wall: int
room_id: list[int] = field(default_factory=list)
@dataclass
class B01Props(RoborockBase):
"""
Represents the complete properties and status for a Roborock B01 model.
This dataclass is generated based on the device's status JSON object.
"""
status: WorkStatusMapping | None = None
fault: B01Fault | None = None
wind: SCWindMapping | None = None
water: WaterLevelMapping | None = None
mode: CleanTypeMapping | None = None
quantity: int | None = None
alarm: int | None = None
volume: int | None = None
hypa: int | None = None
main_brush: int | None = None
side_brush: int | None = None
mop_life: int | None = None
main_sensor: int | None = None
net_status: NetStatus | None = None
repeat_state: CleanRepeatMapping | None = None
tank_state: int | None = None
sweep_type: int | None = None
clean_path_preference: CleanPathPreferenceMapping | None = None
cloth_state: int | None = None
time_zone: int | None = None
time_zone_info: str | None = None
language: int | None = None
cleaning_time: int | None = None
real_clean_time: int | None = None
cleaning_area: int | None = None
custom_type: int | None = None
sound: int | None = None
work_mode: WorkModeMapping | None = None
station_act: int | None = None
charge_state: int | None = None
current_map_id: int | None = None
map_num: int | None = None
dust_action: int | None = None
quiet_is_open: int | None = None
quiet_begin_time: int | None = None
quiet_end_time: int | None = None
clean_finish: int | None = None
voice_type: int | None = None
voice_type_version: int | None = None
order_total: OrderTotal | None = None
build_map: int | None = None
privacy: Privacy | None = None
dust_auto_state: int | None = None
dust_frequency: int | None = None
child_lock: int | None = None
multi_floor: int | None = None
map_save: int | None = None
light_mode: int | None = None
green_laser: int | None = None
dust_bag_used: int | None = None
order_save_mode: int | None = None
manufacturer: str | None = None
back_to_wash: int | None = None
charge_station_type: int | None = None
pv_cut_charge: int | None = None
pv_charging: PvCharging | None = None
serial_number: str | None = None
recommend: Recommend | None = None
add_sweep_status: int | None = None
@property
def main_brush_time_left(self) -> int | None:
"""
Returns estimated remaining life of the main brush in minutes.
Total life is 300 hours (18000 minutes).
"""
if self.main_brush is None:
return None
return max(0, 18000 - self.main_brush)
@property
def side_brush_time_left(self) -> int | None:
"""
Returns estimated remaining life of the side brush in minutes.
Total life is 200 hours (12000 minutes).
"""
if self.side_brush is None:
return None
return max(0, 12000 - self.side_brush)
@property
def filter_time_left(self) -> int | None:
"""
Returns estimated remaining life of the filter (hypa) in minutes.
Total life is 150 hours (9000 minutes).
"""
if self.hypa is None:
return None
return max(0, 9000 - self.hypa)
@property
def mop_life_time_left(self) -> int | None:
"""
Returns estimated remaining life of the mop in minutes.
Total life is 180 hours (10800 minutes).
"""
if self.mop_life is None:
return None
return max(0, 10800 - self.mop_life)
@property
def sensor_dirty_time_left(self) -> int | None:
"""
Returns estimated time until sensors need cleaning in minutes.
Maintenance interval is typically 30 hours (1800 minutes).
"""
if self.main_sensor is None:
return None
return max(0, 1800 - self.main_sensor)
@property
def status_name(self) -> str | None:
"""Returns the name of the current status."""
return self.status.value if self.status is not None else None
@property
def fault_name(self) -> str | None:
"""Returns the name of the current fault."""
return self.fault.value if self.fault is not None else None
@property
def wind_name(self) -> str | None:
"""Returns the name of the current fan speed (wind)."""
return self.wind.value if self.wind is not None else None
@property
def work_mode_name(self) -> str | None:
"""Returns the name of the current work mode."""
return self.work_mode.value if self.work_mode is not None else None
@property
def repeat_state_name(self) -> str | None:
"""Returns the name of the current repeat state."""
return self.repeat_state.value if self.repeat_state is not None else None
@property
def clean_path_preference_name(self) -> str | None:
"""Returns the name of the current clean path preference."""
return self.clean_path_preference.value if self.clean_path_preference is not None else None
@dataclass
class CleanRecordDetail(RoborockBase):
"""Represents a single clean record detail (from `record_list[].detail`)."""
record_start_time: int | None = None
method: int | None = None
record_use_time: int | None = None
clean_count: int | None = None
# This is seemingly returned in meters (non-squared)
record_clean_area: int | None = None
record_clean_mode: int | None = None
record_clean_way: int | None = None
record_task_status: int | None = None
record_faultcode: int | None = None
record_dust_num: int | None = None
clean_current_map: int | None = None
record_map_url: str | None = None
@property
def start_datetime(self) -> datetime.datetime | None:
"""Convert the start datetime into a datetime object."""
if self.record_start_time is not None:
return datetime.datetime.fromtimestamp(self.record_start_time).astimezone(datetime.UTC)
return None
@property
def square_meters_area_cleaned(self) -> float | None:
"""Returns the area cleaned in square meters."""
if self.record_clean_area is not None:
return self.record_clean_area / 100
return None
@dataclass
class CleanRecordListItem(RoborockBase):
"""Represents an entry in the clean record list returned by `service.get_record_list`."""
url: str | None = None
detail: str | None = None
@cached_property
def detail_parsed(self) -> CleanRecordDetail | None:
"""Parse and return the detail as a CleanRecordDetail object."""
if self.detail is None:
return None
try:
parsed = json.loads(self.detail)
except json.JSONDecodeError as ex:
raise RoborockException(f"Invalid B01 record detail JSON: {self.detail!r}") from ex
return CleanRecordDetail.from_dict(parsed)
@dataclass
class CleanRecordList(RoborockBase):
"""Represents the clean record list response from `service.get_record_list`."""
total_area: int | None = None
total_time: int | None = None # stored in seconds
total_count: int | None = None
record_list: list[CleanRecordListItem] = field(default_factory=list)
@property
def square_meters_area_cleaned(self) -> float | None:
"""Returns the area cleaned in square meters."""
if self.total_area is not None:
return self.total_area / 100
return None
@dataclass
class CleanRecordSummary(RoborockBase):
"""Represents clean record totals for B01/Q7 devices."""
total_time: int | None = None
total_area: int | None = None
total_count: int | None = None
last_record_detail: CleanRecordDetail | None = None
|