roborock.data.b01_q7.b01_q7_containers

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

Represents the network status of the device.

NetStatus( rssi: str, loss: int, ping: int, ip: str, mac: str, ssid: str, frequency: int, bssid: str)
rssi: str
loss: int
ping: int
ip: str
mac: str
ssid: str
frequency: int
bssid: str
@dataclass
class OrderTotal(roborock.data.containers.RoborockBase):
35@dataclass
36class OrderTotal(RoborockBase):
37    """Represents the order total information."""
38
39    total: int
40    enable: int

Represents the order total information.

OrderTotal(total: int, enable: int)
total: int
enable: int
@dataclass
class Privacy(roborock.data.containers.RoborockBase):
43@dataclass
44class Privacy(RoborockBase):
45    """Represents the privacy settings of the device."""
46
47    ai_recognize: int
48    dirt_recognize: int
49    pet_recognize: int
50    carpet_turbo: int
51    carpet_avoid: int
52    carpet_show: int
53    map_uploads: int
54    ai_agent: int
55    ai_avoidance: int
56    record_uploads: int
57    along_floor: int
58    auto_upgrade: int

Represents the privacy settings of the device.

Privacy( 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)
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(roborock.data.containers.RoborockBase):
61@dataclass
62class PvCharging(RoborockBase):
63    """Represents the photovoltaic charging status."""
64
65    status: int
66    begin_time: int
67    end_time: int

Represents the photovoltaic charging status.

PvCharging(status: int, begin_time: int, end_time: int)
status: int
begin_time: int
end_time: int
@dataclass
class Recommend(roborock.data.containers.RoborockBase):
70@dataclass
71class Recommend(RoborockBase):
72    """Represents cleaning recommendations."""
73
74    sill: int
75    wall: int
76    room_id: list[int] = field(default_factory=list)

Represents cleaning recommendations.

Recommend(sill: int, wall: int, room_id: list[int] = <factory>)
sill: int
wall: int
room_id: list[int]
@dataclass
class B01Props(roborock.data.containers.RoborockBase):
 79@dataclass
 80class B01Props(RoborockBase):
 81    """
 82    Represents the complete properties and status for a Roborock B01 model.
 83    This dataclass is generated based on the device's status JSON object.
 84    """
 85
 86    status: WorkStatusMapping | None = None
 87    fault: B01Fault | None = None
 88    wind: SCWindMapping | None = None
 89    water: WaterLevelMapping | None = None
 90    mode: CleanTypeMapping | None = None
 91    quantity: int | None = None
 92    alarm: int | None = None
 93    volume: int | None = None
 94    hypa: int | None = None
 95    main_brush: int | None = None
 96    side_brush: int | None = None
 97    mop_life: int | None = None
 98    main_sensor: int | None = None
 99    net_status: NetStatus | None = None
100    repeat_state: CleanRepeatMapping | None = None
101    tank_state: int | None = None
102    sweep_type: int | None = None
103    clean_path_preference: CleanPathPreferenceMapping | None = None
104    cloth_state: int | None = None
105    time_zone: int | None = None
106    time_zone_info: str | None = None
107    language: int | None = None
108    cleaning_time: int | None = None
109    real_clean_time: int | None = None
110    cleaning_area: int | None = None
111    custom_type: int | None = None
112    sound: int | None = None
113    work_mode: WorkModeMapping | None = None
114    station_act: int | None = None
115    charge_state: int | None = None
116    current_map_id: int | None = None
117    map_num: int | None = None
118    dust_action: int | None = None
119    quiet_is_open: int | None = None
120    quiet_begin_time: int | None = None
121    quiet_end_time: int | None = None
122    clean_finish: int | None = None
123    voice_type: int | None = None
124    voice_type_version: int | None = None
125    order_total: OrderTotal | None = None
126    build_map: int | None = None
127    privacy: Privacy | None = None
128    dust_auto_state: int | None = None
129    dust_frequency: int | None = None
130    child_lock: int | None = None
131    multi_floor: int | None = None
132    map_save: int | None = None
133    light_mode: int | None = None
134    green_laser: int | None = None
135    dust_bag_used: int | None = None
136    order_save_mode: int | None = None
137    manufacturer: str | None = None
138    back_to_wash: int | None = None
139    charge_station_type: int | None = None
140    pv_cut_charge: int | None = None
141    pv_charging: PvCharging | None = None
142    serial_number: str | None = None
143    recommend: Recommend | None = None
144    add_sweep_status: int | None = None
145
146    @property
147    def main_brush_time_left(self) -> int | None:
148        """
149        Returns estimated remaining life of the main brush in minutes.
150        Total life is 300 hours (18000 minutes).
151        """
152        if self.main_brush is None:
153            return None
154        return max(0, 18000 - self.main_brush)
155
156    @property
157    def side_brush_time_left(self) -> int | None:
158        """
159        Returns estimated remaining life of the side brush in minutes.
160        Total life is 200 hours (12000 minutes).
161        """
162        if self.side_brush is None:
163            return None
164        return max(0, 12000 - self.side_brush)
165
166    @property
167    def filter_time_left(self) -> int | None:
168        """
169        Returns estimated remaining life of the filter (hypa) in minutes.
170        Total life is 150 hours (9000 minutes).
171        """
172        if self.hypa is None:
173            return None
174        return max(0, 9000 - self.hypa)
175
176    @property
177    def mop_life_time_left(self) -> int | None:
178        """
179        Returns estimated remaining life of the mop in minutes.
180        Total life is 180 hours (10800 minutes).
181        """
182        if self.mop_life is None:
183            return None
184        return max(0, 10800 - self.mop_life)
185
186    @property
187    def sensor_dirty_time_left(self) -> int | None:
188        """
189        Returns estimated time until sensors need cleaning in minutes.
190        Maintenance interval is typically 30 hours (1800 minutes).
191        """
192        if self.main_sensor is None:
193            return None
194        return max(0, 1800 - self.main_sensor)
195
196    @property
197    def status_name(self) -> str | None:
198        """Returns the name of the current status."""
199        return self.status.value if self.status is not None else None
200
201    @property
202    def fault_name(self) -> str | None:
203        """Returns the name of the current fault."""
204        return self.fault.value if self.fault is not None else None
205
206    @property
207    def wind_name(self) -> str | None:
208        """Returns the name of the current fan speed (wind)."""
209        return self.wind.value if self.wind is not None else None
210
211    @property
212    def work_mode_name(self) -> str | None:
213        """Returns the name of the current work mode."""
214        return self.work_mode.value if self.work_mode is not None else None
215
216    @property
217    def repeat_state_name(self) -> str | None:
218        """Returns the name of the current repeat state."""
219        return self.repeat_state.value if self.repeat_state is not None else None
220
221    @property
222    def clean_path_preference_name(self) -> str | None:
223        """Returns the name of the current clean path preference."""
224        return self.clean_path_preference.value if self.clean_path_preference is not None else None

Represents the complete properties and status for a Roborock B01 model. This dataclass is generated based on the device's status JSON object.

B01Props( status: roborock.data.b01_q7.b01_q7_code_mappings.WorkStatusMapping | None = None, fault: roborock.data.b01_q7.b01_q7_code_mappings.B01Fault | None = None, wind: roborock.data.b01_q7.b01_q7_code_mappings.SCWindMapping | None = None, water: roborock.data.b01_q7.b01_q7_code_mappings.WaterLevelMapping | None = None, mode: roborock.data.b01_q7.b01_q7_code_mappings.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: roborock.data.b01_q7.b01_q7_code_mappings.CleanRepeatMapping | None = None, tank_state: int | None = None, sweep_type: int | None = None, clean_path_preference: roborock.data.b01_q7.b01_q7_code_mappings.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: roborock.data.b01_q7.b01_q7_code_mappings.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)
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
tank_state: int | None = None
sweep_type: int | 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
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
main_brush_time_left: int | None
146    @property
147    def main_brush_time_left(self) -> int | None:
148        """
149        Returns estimated remaining life of the main brush in minutes.
150        Total life is 300 hours (18000 minutes).
151        """
152        if self.main_brush is None:
153            return None
154        return max(0, 18000 - self.main_brush)

Returns estimated remaining life of the main brush in minutes. Total life is 300 hours (18000 minutes).

side_brush_time_left: int | None
156    @property
157    def side_brush_time_left(self) -> int | None:
158        """
159        Returns estimated remaining life of the side brush in minutes.
160        Total life is 200 hours (12000 minutes).
161        """
162        if self.side_brush is None:
163            return None
164        return max(0, 12000 - self.side_brush)

Returns estimated remaining life of the side brush in minutes. Total life is 200 hours (12000 minutes).

filter_time_left: int | None
166    @property
167    def filter_time_left(self) -> int | None:
168        """
169        Returns estimated remaining life of the filter (hypa) in minutes.
170        Total life is 150 hours (9000 minutes).
171        """
172        if self.hypa is None:
173            return None
174        return max(0, 9000 - self.hypa)

Returns estimated remaining life of the filter (hypa) in minutes. Total life is 150 hours (9000 minutes).

mop_life_time_left: int | None
176    @property
177    def mop_life_time_left(self) -> int | None:
178        """
179        Returns estimated remaining life of the mop in minutes.
180        Total life is 180 hours (10800 minutes).
181        """
182        if self.mop_life is None:
183            return None
184        return max(0, 10800 - self.mop_life)

Returns estimated remaining life of the mop in minutes. Total life is 180 hours (10800 minutes).

sensor_dirty_time_left: int | None
186    @property
187    def sensor_dirty_time_left(self) -> int | None:
188        """
189        Returns estimated time until sensors need cleaning in minutes.
190        Maintenance interval is typically 30 hours (1800 minutes).
191        """
192        if self.main_sensor is None:
193            return None
194        return max(0, 1800 - self.main_sensor)

Returns estimated time until sensors need cleaning in minutes. Maintenance interval is typically 30 hours (1800 minutes).

status_name: str | None
196    @property
197    def status_name(self) -> str | None:
198        """Returns the name of the current status."""
199        return self.status.value if self.status is not None else None

Returns the name of the current status.

fault_name: str | None
201    @property
202    def fault_name(self) -> str | None:
203        """Returns the name of the current fault."""
204        return self.fault.value if self.fault is not None else None

Returns the name of the current fault.

wind_name: str | None
206    @property
207    def wind_name(self) -> str | None:
208        """Returns the name of the current fan speed (wind)."""
209        return self.wind.value if self.wind is not None else None

Returns the name of the current fan speed (wind).

work_mode_name: str | None
211    @property
212    def work_mode_name(self) -> str | None:
213        """Returns the name of the current work mode."""
214        return self.work_mode.value if self.work_mode is not None else None

Returns the name of the current work mode.

repeat_state_name: str | None
216    @property
217    def repeat_state_name(self) -> str | None:
218        """Returns the name of the current repeat state."""
219        return self.repeat_state.value if self.repeat_state is not None else None

Returns the name of the current repeat state.

clean_path_preference_name: str | None
221    @property
222    def clean_path_preference_name(self) -> str | None:
223        """Returns the name of the current clean path preference."""
224        return self.clean_path_preference.value if self.clean_path_preference is not None else None

Returns the name of the current clean path preference.

@dataclass
class CleanRecordDetail(roborock.data.containers.RoborockBase):
227@dataclass
228class CleanRecordDetail(RoborockBase):
229    """Represents a single clean record detail (from `record_list[].detail`)."""
230
231    record_start_time: int | None = None
232    method: int | None = None
233    record_use_time: int | None = None
234    clean_count: int | None = None
235    # This is seemingly returned in meters (non-squared)
236    record_clean_area: int | None = None
237    record_clean_mode: int | None = None
238    record_clean_way: int | None = None
239    record_task_status: int | None = None
240    record_faultcode: int | None = None
241    record_dust_num: int | None = None
242    clean_current_map: int | None = None
243    record_map_url: str | None = None
244
245    @property
246    def start_datetime(self) -> datetime.datetime | None:
247        """Convert the start datetime into a datetime object."""
248        if self.record_start_time is not None:
249            return datetime.datetime.fromtimestamp(self.record_start_time).astimezone(datetime.UTC)
250        return None
251
252    @property
253    def square_meters_area_cleaned(self) -> float | None:
254        """Returns the area cleaned in square meters."""
255        if self.record_clean_area is not None:
256            return self.record_clean_area / 100
257        return None

Represents a single clean record detail (from record_list[].detail).

CleanRecordDetail( record_start_time: int | None = None, method: int | None = None, record_use_time: int | None = None, clean_count: int | None = None, 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)
record_start_time: int | None = None
method: int | None = None
record_use_time: int | None = None
clean_count: int | None = None
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
start_datetime: datetime.datetime | None
245    @property
246    def start_datetime(self) -> datetime.datetime | None:
247        """Convert the start datetime into a datetime object."""
248        if self.record_start_time is not None:
249            return datetime.datetime.fromtimestamp(self.record_start_time).astimezone(datetime.UTC)
250        return None

Convert the start datetime into a datetime object.

square_meters_area_cleaned: float | None
252    @property
253    def square_meters_area_cleaned(self) -> float | None:
254        """Returns the area cleaned in square meters."""
255        if self.record_clean_area is not None:
256            return self.record_clean_area / 100
257        return None

Returns the area cleaned in square meters.

@dataclass
class CleanRecordListItem(roborock.data.containers.RoborockBase):
260@dataclass
261class CleanRecordListItem(RoborockBase):
262    """Represents an entry in the clean record list returned by `service.get_record_list`."""
263
264    url: str | None = None
265    detail: str | None = None
266
267    @cached_property
268    def detail_parsed(self) -> CleanRecordDetail | None:
269        """Parse and return the detail as a CleanRecordDetail object."""
270        if self.detail is None:
271            return None
272        try:
273            parsed = json.loads(self.detail)
274        except json.JSONDecodeError as ex:
275            raise RoborockException(f"Invalid B01 record detail JSON: {self.detail!r}") from ex
276        return CleanRecordDetail.from_dict(parsed)

Represents an entry in the clean record list returned by service.get_record_list.

CleanRecordListItem(url: str | None = None, detail: str | None = None)
url: str | None = None
detail: str | None = None
detail_parsed: CleanRecordDetail | None
267    @cached_property
268    def detail_parsed(self) -> CleanRecordDetail | None:
269        """Parse and return the detail as a CleanRecordDetail object."""
270        if self.detail is None:
271            return None
272        try:
273            parsed = json.loads(self.detail)
274        except json.JSONDecodeError as ex:
275            raise RoborockException(f"Invalid B01 record detail JSON: {self.detail!r}") from ex
276        return CleanRecordDetail.from_dict(parsed)

Parse and return the detail as a CleanRecordDetail object.

@dataclass
class CleanRecordList(roborock.data.containers.RoborockBase):
279@dataclass
280class CleanRecordList(RoborockBase):
281    """Represents the clean record list response from `service.get_record_list`."""
282
283    total_area: int | None = None
284    total_time: int | None = None  # stored in seconds
285    total_count: int | None = None
286    record_list: list[CleanRecordListItem] = field(default_factory=list)
287
288    @property
289    def square_meters_area_cleaned(self) -> float | None:
290        """Returns the area cleaned in square meters."""
291        if self.total_area is not None:
292            return self.total_area / 100
293        return None

Represents the clean record list response from service.get_record_list.

CleanRecordList( total_area: int | None = None, total_time: int | None = None, total_count: int | None = None, record_list: list[CleanRecordListItem] = <factory>)
total_area: int | None = None
total_time: int | None = None
total_count: int | None = None
record_list: list[CleanRecordListItem]
square_meters_area_cleaned: float | None
288    @property
289    def square_meters_area_cleaned(self) -> float | None:
290        """Returns the area cleaned in square meters."""
291        if self.total_area is not None:
292            return self.total_area / 100
293        return None

Returns the area cleaned in square meters.

@dataclass
class CleanRecordSummary(roborock.data.containers.RoborockBase):
296@dataclass
297class CleanRecordSummary(RoborockBase):
298    """Represents clean record totals for B01/Q7 devices."""
299
300    total_time: int | None = None
301    total_area: int | None = None
302    total_count: int | None = None
303    last_record_detail: CleanRecordDetail | None = None

Represents clean record totals for B01/Q7 devices.

CleanRecordSummary( total_time: int | None = None, total_area: int | None = None, total_count: int | None = None, last_record_detail: CleanRecordDetail | None = None)
total_time: int | None = None
total_area: int | None = None
total_count: int | None = None
last_record_detail: CleanRecordDetail | None = None