Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 33 additions & 46 deletions roborock/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,32 @@ def _decamelize(s: str):
return re.sub("([A-Z]+)", "_\\1", s).lower()


def _attr_repr(obj: Any, attrs: list[str]) -> str:
"""Return a string representation of the object including specified attributes."""
def _attr_repr(obj: Any) -> str:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this just become the default repr for RoborockBase?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but it seemed to not see child attributes for some reason. (see comment above)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It could be that it is specific to the traits with mixins since those are the ones I am testing, but other way, this did seem to address that)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. Thanks, I see your other comment now. Yep we can revisit in the future if needed.

"""Return a string representation of the object including specified attributes.

This reproduces the default repr behavior of dataclasses, but also includes
properties. This must be called by the child class's __repr__ method since
the parent RoborockBase class does not know about the child class's attributes.
"""
# Reproduce default repr behavior
items = (f"{k}={v!r}" for k, v in obj.__dict__.items() if not k.startswith("_"))
default_repr = "{}({})".format(type(obj).__name__, ", ".join(items))
# Append additional attributes
parts = [default_repr[:-1]]
for attr in attrs:
value = getattr(obj, attr, None)
parts.append(f", {attr}={repr(value)}")
parts.append(")")
return "".join(parts)


@dataclass
parts = []
for k in dir(obj):
if k.startswith("_"):
continue
try:
v = getattr(obj, k)
except (RuntimeError, Exception):
continue
if callable(v):
continue
parts.append(f"{k}={v!r}")
return f"{type(obj).__name__}({', '.join(parts)})"


@dataclass(repr=False)
class RoborockBase:
"""Base class for all Roborock data classes."""

@staticmethod
def _convert_to_class_obj(class_type: type, value):
if get_origin(class_type) is list:
Expand Down Expand Up @@ -208,7 +218,7 @@ def end_time(self) -> datetime.time | None:
)

def __repr__(self) -> str:
return _attr_repr(self, ["start_time", "end_time"])
return _attr_repr(self)


@dataclass
Expand Down Expand Up @@ -462,18 +472,7 @@ def current_map(self) -> int | None:
return None

def __repr__(self) -> str:
return _attr_repr(
self,
[
"square_meter_clean_area",
"error_code_name",
"state_name",
"water_box_mode_name",
"fan_power_name",
"mop_mode_name",
"current_map",
],
)
return _attr_repr(self)


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

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


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

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


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

def __repr__(self) -> str:
return _attr_repr(
self,
[
"main_brush_time_left",
"side_brush_time_left",
"filter_time_left",
"sensor_time_left",
"strainer_time_left",
"dust_collection_time_left",
"cleaning_brush_time_left",
"mop_roller_time_left",
],
)

return _attr_repr(self)

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

def __repr__(self) -> str:
return _attr_repr(self, ["product_nickname"])
return _attr_repr(self)


@dataclass
Expand Down Expand Up @@ -923,7 +910,7 @@ def product_nickname(self) -> RoborockProductNickname | None:
return None

def __repr__(self) -> str:
return _attr_repr(self, ["product_nickname"])
return _attr_repr(self)


@dataclass
Expand Down
4 changes: 2 additions & 2 deletions tests/devices/__snapshots__/test_v1_device.ambr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# serializer version: 1
# name: test_device_trait_command_parsing[payload0-<lambda>]
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)
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)
# ---
# name: test_device_trait_command_parsing[payload1-<lambda>]
DoNotDisturbTrait(start_hour=22, start_minute=0, end_hour=8, end_minute=0, enabled=1)
# ---
# name: test_device_trait_command_parsing[payload2-<lambda>]
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)
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)
# ---
# name: test_device_trait_command_parsing[payload3-<lambda>]
SoundVolumeTrait(volume=90)
Expand Down
Loading