Skip to content

Commit 9a1cda3

Browse files
committed
chore: update to get all properties at runtime
1 parent 1bc5824 commit 9a1cda3

File tree

2 files changed

+35
-48
lines changed

2 files changed

+35
-48
lines changed

roborock/containers.py

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -107,22 +107,32 @@ def _decamelize(s: str):
107107
return re.sub("([A-Z]+)", "_\\1", s).lower()
108108

109109

110-
def _attr_repr(obj: Any, attrs: list[str]) -> str:
111-
"""Return a string representation of the object including specified attributes."""
110+
def _attr_repr(obj: Any) -> str:
111+
"""Return a string representation of the object including specified attributes.
112+
113+
This reproduces the default repr behavior of dataclasses, but also includes
114+
properties. This must be called by the child class's __repr__ method since
115+
the parent RoborockBase class does not know about the child class's attributes.
116+
"""
112117
# Reproduce default repr behavior
113-
items = (f"{k}={v!r}" for k, v in obj.__dict__.items() if not k.startswith("_"))
114-
default_repr = "{}({})".format(type(obj).__name__, ", ".join(items))
115-
# Append additional attributes
116-
parts = [default_repr[:-1]]
117-
for attr in attrs:
118-
value = getattr(obj, attr, None)
119-
parts.append(f", {attr}={repr(value)}")
120-
parts.append(")")
121-
return "".join(parts)
122-
123-
124-
@dataclass
118+
parts = []
119+
for k in dir(obj):
120+
if k.startswith("_"):
121+
continue
122+
try:
123+
v = getattr(obj, k)
124+
except (RuntimeError, Exception):
125+
continue
126+
if callable(v):
127+
continue
128+
parts.append(f"{k}={v!r}")
129+
return f"{type(obj).__name__}({', '.join(parts)})"
130+
131+
132+
@dataclass(repr=False)
125133
class RoborockBase:
134+
"""Base class for all Roborock data classes."""
135+
126136
@staticmethod
127137
def _convert_to_class_obj(class_type: type, value):
128138
if get_origin(class_type) is list:
@@ -208,7 +218,7 @@ def end_time(self) -> datetime.time | None:
208218
)
209219

210220
def __repr__(self) -> str:
211-
return _attr_repr(self, ["start_time", "end_time"])
221+
return _attr_repr(self)
212222

213223

214224
@dataclass
@@ -462,18 +472,7 @@ def current_map(self) -> int | None:
462472
return None
463473

464474
def __repr__(self) -> str:
465-
return _attr_repr(
466-
self,
467-
[
468-
"square_meter_clean_area",
469-
"error_code_name",
470-
"state_name",
471-
"water_box_mode_name",
472-
"fan_power_name",
473-
"mop_mode_name",
474-
"current_map",
475-
],
476-
)
475+
return _attr_repr(self)
477476

478477

479478
@dataclass
@@ -638,8 +637,9 @@ def square_meter_clean_area(self) -> float | None:
638637
return None
639638
return round(self.clean_area / 1000000, 1) if self.clean_area is not None else None
640639

641-
def __repr__(self):
642-
return _attr_repr(self, ["square_meter_clean_area"])
640+
def __repr__(self) -> str:
641+
"""Return a string representation of the object including all attributes."""
642+
return _attr_repr(self)
643643

644644

645645
@dataclass
@@ -671,7 +671,7 @@ def end_datetime(self) -> datetime.datetime | None:
671671
return datetime.datetime.fromtimestamp(self.end).astimezone(timezone.utc) if self.end else None
672672

673673
def __repr__(self) -> str:
674-
return _attr_repr(self, ["square_meter_area", "begin_datetime", "end_datetime"])
674+
return _attr_repr(self)
675675

676676

677677
@dataclass
@@ -727,20 +727,7 @@ def mop_roller_time_left(self) -> int | None:
727727
return MOP_ROLLER_REPLACE_TIME - self.moproller_work_time if self.moproller_work_time is not None else None
728728

729729
def __repr__(self) -> str:
730-
return _attr_repr(
731-
self,
732-
[
733-
"main_brush_time_left",
734-
"side_brush_time_left",
735-
"filter_time_left",
736-
"sensor_time_left",
737-
"strainer_time_left",
738-
"dust_collection_time_left",
739-
"cleaning_brush_time_left",
740-
"mop_roller_time_left",
741-
],
742-
)
743-
730+
return _attr_repr(self)
744731

745732
@dataclass
746733
class MultiMapsListMapInfoBakMaps(RoborockBase):
@@ -830,7 +817,7 @@ def product_nickname(self) -> RoborockProductNickname:
830817
return SHORT_MODEL_TO_ENUM.get(self.model.split(".")[-1], RoborockProductNickname.PEARLPLUS)
831818

832819
def __repr__(self) -> str:
833-
return _attr_repr(self, ["product_nickname"])
820+
return _attr_repr(self)
834821

835822

836823
@dataclass
@@ -923,7 +910,7 @@ def product_nickname(self) -> RoborockProductNickname | None:
923910
return None
924911

925912
def __repr__(self) -> str:
926-
return _attr_repr(self, ["product_nickname"])
913+
return _attr_repr(self)
927914

928915

929916
@dataclass

tests/devices/__snapshots__/test_v1_device.ambr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# serializer version: 1
22
# name: test_device_trait_command_parsing[payload0-<lambda>]
3-
StatusTrait(msg_ver=2, msg_seq=515, state=<RoborockStateCode.charging: 8>, battery=100, clean_time=5405, clean_area=91287500, error_code=<RoborockErrorCode.none: 0>, map_present=1, in_cleaning=<RoborockInCleaning.complete: 0>, in_returning=0, in_fresh_state=1, lab_status=1, water_box_status=0, fan_power=<RoborockFanSpeedS7MaxV.custom: 106>, dnd_enabled=1, map_status=3, is_locating=0, lock_status=0, water_box_mode=<RoborockMopIntensityS7.custom: 204>, water_box_carriage_status=0, mop_forbidden_enable=0, unsave_map_reason=4, unsave_map_flag=0, distance_off=0, square_meter_clean_area=91.3, error_code_name=None, state_name='charging', water_box_mode_name='custom', fan_power_name='custom', mop_mode_name=None, current_map=0)
3+
StatusTrait(adbumper_status=None, auto_dust_collection=None, avoid_count=None, back_type=None, battery=100, camera_status=None, charge_status=None, clean_area=91287500, clean_percent=None, clean_time=5405, collision_avoid_status=None, command=<RoborockCommand.GET_STATUS: 'get_status'>, common_status=None, corner_clean_mode=None, current_map=0, debug_mode=None, distance_off=0, dnd_enabled=1, dock_error_status=None, dock_type=None, dry_status=None, dss=None, dust_collection_status=None, error_code=<RoborockErrorCode.none: 0>, error_code_name=None, fan_power=<RoborockFanSpeedS7MaxV.custom: 106>, fan_power_name='custom', fan_power_options=['off', 'quiet', 'balanced', 'turbo', 'max', 'custom', 'max_plus'], home_sec_enable_password=None, home_sec_status=None, in_cleaning=<RoborockInCleaning.complete: 0>, in_fresh_state=1, in_returning=0, in_warmup=None, is_exploring=None, is_locating=0, lab_status=1, lock_status=0, map_present=1, map_status=3, mop_forbidden_enable=0, mop_mode=None, mop_mode_name=None, msg_seq=515, msg_ver=2, rdt=None, rss=None, square_meter_clean_area=91.3, state=<RoborockStateCode.charging: 8>, state_name='charging', switch_map_mode=None, unsave_map_flag=0, unsave_map_reason=4, wash_phase=None, wash_ready=None, wash_status=None, water_box_carriage_status=0, water_box_mode=<RoborockMopIntensityS7.custom: 204>, water_box_mode_name='custom', water_box_status=0, water_shortage_status=None)
44
# ---
55
# name: test_device_trait_command_parsing[payload1-<lambda>]
66
DoNotDisturbTrait(start_hour=22, start_minute=0, end_hour=8, end_minute=0, enabled=1)
77
# ---
88
# name: test_device_trait_command_parsing[payload2-<lambda>]
9-
CleanSummaryTrait(clean_time=1442559, clean_area=24258125000, clean_count=296, dust_collection_count=None, records=[1756848207, 1754930385, 1753203976, 1752183435, 1747427370, 1746204046, 1745601543, 1744387080, 1743528522, 1742489154, 1741022299, 1740433682, 1739902516, 1738875106, 1738864366, 1738620067, 1736873889, 1736197544, 1736121269, 1734458038], last_clean_t=None, square_meter_clean_area=24258.1)
9+
CleanSummaryTrait(clean_area=24258125000, clean_count=296, clean_time=1442559, command=<RoborockCommand.GET_CLEAN_SUMMARY: 'get_clean_summary'>, dust_collection_count=None, last_clean_t=None, records=[1756848207, 1754930385, 1753203976, 1752183435, 1747427370, 1746204046, 1745601543, 1744387080, 1743528522, 1742489154, 1741022299, 1740433682, 1739902516, 1738875106, 1738864366, 1738620067, 1736873889, 1736197544, 1736121269, 1734458038], square_meter_clean_area=24258.1)
1010
# ---
1111
# name: test_device_trait_command_parsing[payload3-<lambda>]
1212
SoundVolumeTrait(volume=90)

0 commit comments

Comments
 (0)