From 230e2f789b585a57d8b67d08c64242e196494644 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 28 Apr 2023 12:36:02 -0400 Subject: [PATCH 1/3] feat: add support for old mop and vacuum codes --- roborock/api.py | 4 +++- roborock/code_mappings.py | 32 +++++++++++++++++++++++++++++++- roborock/containers.py | 11 +++++++++++ roborock/typing.py | 1 + tests/test_containers.py | 12 ++++++++++-- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/roborock/api.py b/roborock/api.py index bcda9a26..12ed22cc 100644 --- a/roborock/api.py +++ b/roborock/api.py @@ -35,7 +35,7 @@ SmartWashParams, Status, UserData, - WashTowelMode, + WashTowelMode, StatusOldModes, ) from .exceptions import ( RoborockAccountDoesNotExist, @@ -206,6 +206,8 @@ async def send_command(self, device_id: str, method: RoborockCommand, params: Op async def get_status(self, device_id: str) -> Status | None: status = await self.send_command(device_id, RoborockCommand.GET_STATUS) if isinstance(status, dict): + if self.devices_info[device_id].device.uses_old_codes: + return StatusOldModes.from_dict(status) return Status.from_dict(status) return None diff --git a/roborock/code_mappings.py b/roborock/code_mappings.py index 9bdd3cb5..c60aa108 100644 --- a/roborock/code_mappings.py +++ b/roborock/code_mappings.py @@ -160,7 +160,14 @@ def create_code_enum(name: str, data: dict) -> RoborockEnum: RoborockDockTypeCode = create_code_enum( "RoborockDockTypeCode", - {0: "no_dock", 1: "unknown", 2: "unknown", 3: "empty_wash_fill_dock", 4: "unknown", 5: "auto_empty_dock_pure"}, + { + 0: "no_dock", + 1: "unknown", + 2: "unknown", + 3: "empty_wash_fill_dock", + 4: "unknown", + 5: "auto_empty_dock_pure" + }, ) RoborockDockDustCollectionModeCode = create_code_enum( @@ -181,3 +188,26 @@ def create_code_enum(name: str, data: dict) -> RoborockEnum: 2: "deep", }, ) + +OldRoborockFanPowerCode = create_code_enum( + "OldRoborockFanPowerCode", + { + 101: "silent", + 102: "balanced", + 103: "turbo", + 104: "max", + 105: "gentle", + 106: "customize (auto)" + } +) + +OldRoborockMopIntensityCode = create_code_enum( + "OldRoborockMopModeCode", + { + 200: "off", + 201: "low", + 202: "medium", + 203: "high", + 207: "custom (levels)" + } +) diff --git a/roborock/containers.py b/roborock/containers.py index 0ccf7846..bdec9baf 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -20,6 +20,8 @@ RoborockErrorCode, RoborockFanPowerCode, RoborockMopModeCode, + OldRoborockFanPowerCode, + OldRoborockMopIntensityCode, ) from .const import FILTER_REPLACE_TIME, MAIN_BRUSH_REPLACE_TIME, SENSOR_DIRTY_REPLACE_TIME, SIDE_BRUSH_REPLACE_TIME @@ -160,6 +162,11 @@ class HomeDataDevice(RoborockBase): new_feature_set: Optional[str] = None device_status: Optional[HomeDataDeviceStatus] = None silent_ota_switch: Optional[bool] = None + uses_old_codes: bool = False + + def __post_init__(self): + if self.device_status.model == "roborock.vacuum.a10": + self.uses_old_codes = True @dataclass @@ -234,6 +241,10 @@ class Status(RoborockBase): unsave_map_reason: Optional[int] = None unsave_map_flag: Optional[int] = None +class StatusOldModes(Status): + water_box_mode: Optional[OldRoborockMopIntensityCode] = None # type: ignore[valid-type] + fan_power: Optional[OldRoborockFanPowerCode] = None # type: ignore[valid-type] + @dataclass class DNDTimer(RoborockBase): diff --git a/roborock/typing.py b/roborock/typing.py index 6a23d202..71b016d4 100644 --- a/roborock/typing.py +++ b/roborock/typing.py @@ -107,6 +107,7 @@ class RoborockCommand(str, Enum): GET_DEVICE_ICE = "get_device_ice" START_VOICE_CHAT = "start_voice_chat" SEND_SDP_TO_ROBOT = "send_sdp_to_robot" + GET_FW_FEATURES = "get_fw_features" @dataclass diff --git a/tests/test_containers.py b/tests/test_containers.py index 0b82c0cb..2b0510c7 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,4 +1,4 @@ -from roborock import CleanRecord, CleanSummary, Consumable, DNDTimer, HomeData, Status, UserData +from roborock import CleanRecord, CleanSummary, Consumable, DNDTimer, HomeData, Status, UserData, StatusOldModes from roborock.code_mappings import ( RoborockDockErrorCode, RoborockDockTypeCode, @@ -6,7 +6,7 @@ RoborockFanPowerCode, RoborockMopIntensityCode, RoborockMopModeCode, - RoborockStateCode, + RoborockStateCode, OldRoborockFanPowerCode, OldRoborockMopIntensityCode, ) from .mock_data import CLEAN_RECORD, CLEAN_SUMMARY, CONSUMABLE, DND_TIMER, HOME_DATA_RAW, STATUS, USER_DATA @@ -152,6 +152,14 @@ def test_status(): assert s.unsave_map_reason == 0 assert s.unsave_map_flag == 0 +def test_old_status(): + s = StatusOldModes.from_dict(STATUS) + assert isinstance(s.water_box_mode, OldRoborockMopIntensityCode) + assert isinstance(s.fan_power, OldRoborockFanPowerCode) + assert s.msg_ver == 2 + assert s.msg_seq == 458 + assert s.state == RoborockStateCode["8"] + assert s.fan_power == OldRoborockFanPowerCode["102"] def test_dnd_timer(): dnd = DNDTimer.from_dict(DND_TIMER) From 908f115b0c1ba74876de572cf4676e7150afba69 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 28 Apr 2023 12:38:31 -0400 Subject: [PATCH 2/3] fix: linting --- roborock/api.py | 3 ++- roborock/code_mappings.py | 27 +++------------------------ roborock/containers.py | 5 +++-- tests/test_containers.py | 8 ++++++-- 4 files changed, 14 insertions(+), 29 deletions(-) diff --git a/roborock/api.py b/roborock/api.py index 12ed22cc..611aaf3d 100644 --- a/roborock/api.py +++ b/roborock/api.py @@ -34,8 +34,9 @@ RoomMapping, SmartWashParams, Status, + StatusOldModes, UserData, - WashTowelMode, StatusOldModes, + WashTowelMode, ) from .exceptions import ( RoborockAccountDoesNotExist, diff --git a/roborock/code_mappings.py b/roborock/code_mappings.py index c60aa108..0be4f681 100644 --- a/roborock/code_mappings.py +++ b/roborock/code_mappings.py @@ -160,14 +160,7 @@ def create_code_enum(name: str, data: dict) -> RoborockEnum: RoborockDockTypeCode = create_code_enum( "RoborockDockTypeCode", - { - 0: "no_dock", - 1: "unknown", - 2: "unknown", - 3: "empty_wash_fill_dock", - 4: "unknown", - 5: "auto_empty_dock_pure" - }, + {0: "no_dock", 1: "unknown", 2: "unknown", 3: "empty_wash_fill_dock", 4: "unknown", 5: "auto_empty_dock_pure"}, ) RoborockDockDustCollectionModeCode = create_code_enum( @@ -191,23 +184,9 @@ def create_code_enum(name: str, data: dict) -> RoborockEnum: OldRoborockFanPowerCode = create_code_enum( "OldRoborockFanPowerCode", - { - 101: "silent", - 102: "balanced", - 103: "turbo", - 104: "max", - 105: "gentle", - 106: "customize (auto)" - } + {101: "silent", 102: "balanced", 103: "turbo", 104: "max", 105: "gentle", 106: "customize (auto)"}, ) OldRoborockMopIntensityCode = create_code_enum( - "OldRoborockMopModeCode", - { - 200: "off", - 201: "low", - 202: "medium", - 203: "high", - 207: "custom (levels)" - } + "OldRoborockMopModeCode", {200: "off", 201: "low", 202: "medium", 203: "high", 207: "custom (levels)"} ) diff --git a/roborock/containers.py b/roborock/containers.py index bdec9baf..109a1b7b 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -15,13 +15,13 @@ ) from .code_mappings import ( + OldRoborockFanPowerCode, + OldRoborockMopIntensityCode, RoborockDockDustCollectionModeCode, RoborockDockErrorCode, RoborockErrorCode, RoborockFanPowerCode, RoborockMopModeCode, - OldRoborockFanPowerCode, - OldRoborockMopIntensityCode, ) from .const import FILTER_REPLACE_TIME, MAIN_BRUSH_REPLACE_TIME, SENSOR_DIRTY_REPLACE_TIME, SIDE_BRUSH_REPLACE_TIME @@ -241,6 +241,7 @@ class Status(RoborockBase): unsave_map_reason: Optional[int] = None unsave_map_flag: Optional[int] = None + class StatusOldModes(Status): water_box_mode: Optional[OldRoborockMopIntensityCode] = None # type: ignore[valid-type] fan_power: Optional[OldRoborockFanPowerCode] = None # type: ignore[valid-type] diff --git a/tests/test_containers.py b/tests/test_containers.py index 2b0510c7..ab63117a 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,12 +1,14 @@ -from roborock import CleanRecord, CleanSummary, Consumable, DNDTimer, HomeData, Status, UserData, StatusOldModes +from roborock import CleanRecord, CleanSummary, Consumable, DNDTimer, HomeData, Status, StatusOldModes, UserData from roborock.code_mappings import ( + OldRoborockFanPowerCode, + OldRoborockMopIntensityCode, RoborockDockErrorCode, RoborockDockTypeCode, RoborockErrorCode, RoborockFanPowerCode, RoborockMopIntensityCode, RoborockMopModeCode, - RoborockStateCode, OldRoborockFanPowerCode, OldRoborockMopIntensityCode, + RoborockStateCode, ) from .mock_data import CLEAN_RECORD, CLEAN_SUMMARY, CONSUMABLE, DND_TIMER, HOME_DATA_RAW, STATUS, USER_DATA @@ -152,6 +154,7 @@ def test_status(): assert s.unsave_map_reason == 0 assert s.unsave_map_flag == 0 + def test_old_status(): s = StatusOldModes.from_dict(STATUS) assert isinstance(s.water_box_mode, OldRoborockMopIntensityCode) @@ -161,6 +164,7 @@ def test_old_status(): assert s.state == RoborockStateCode["8"] assert s.fan_power == OldRoborockFanPowerCode["102"] + def test_dnd_timer(): dnd = DNDTimer.from_dict(DND_TIMER) assert dnd.start_hour == 22 From b2f0295f33db926a646f638cc37dd29352004c63 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 28 Apr 2023 12:40:03 -0400 Subject: [PATCH 3/3] fix: linting --- roborock/containers.py | 4 ++-- tests/test_containers.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/roborock/containers.py b/roborock/containers.py index 109a1b7b..73e0589e 100644 --- a/roborock/containers.py +++ b/roborock/containers.py @@ -127,7 +127,7 @@ class HomeDataDeviceStatus(RoborockBase): id: Optional[Any] = None name: Optional[Any] = None code: Optional[Any] = None - model: Optional[Any] = None + model: Optional[str] = None icon_url: Optional[Any] = None attribute: Optional[Any] = None capability: Optional[Any] = None @@ -165,7 +165,7 @@ class HomeDataDevice(RoborockBase): uses_old_codes: bool = False def __post_init__(self): - if self.device_status.model == "roborock.vacuum.a10": + if self.device_status and self.device_status.model == "roborock.vacuum.a10": self.uses_old_codes = True diff --git a/tests/test_containers.py b/tests/test_containers.py index ab63117a..85859c49 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -1,7 +1,6 @@ from roborock import CleanRecord, CleanSummary, Consumable, DNDTimer, HomeData, Status, StatusOldModes, UserData from roborock.code_mappings import ( OldRoborockFanPowerCode, - OldRoborockMopIntensityCode, RoborockDockErrorCode, RoborockDockTypeCode, RoborockErrorCode, @@ -157,8 +156,6 @@ def test_status(): def test_old_status(): s = StatusOldModes.from_dict(STATUS) - assert isinstance(s.water_box_mode, OldRoborockMopIntensityCode) - assert isinstance(s.fan_power, OldRoborockFanPowerCode) assert s.msg_ver == 2 assert s.msg_seq == 458 assert s.state == RoborockStateCode["8"]