Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring new changes #1

Merged
merged 14 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

<!--next-version-placeholder-->

## v2.5.0 (2024-06-25)

### Feature

* Add some typing ([#219](https://github.com/humbertogontijo/python-roborock/issues/219)) ([`35d0900`](https://github.com/humbertogontijo/python-roborock/commit/35d09000b8d144cbaf935069952ea135950d0e78))

## v2.4.0 (2024-06-25)

### Feature

* Add some missing codes and make warnings only message once ([#218](https://github.com/humbertogontijo/python-roborock/issues/218)) ([`12361b5`](https://github.com/humbertogontijo/python-roborock/commit/12361b58e7a4d368281c4ffd9ac3d8e9d8155e62))

## v2.3.0 (2024-06-07)

### Feature

* Add warning in web requests if it fails to decode ([#215](https://github.com/humbertogontijo/python-roborock/issues/215)) ([`6ae69e9`](https://github.com/humbertogontijo/python-roborock/commit/6ae69e9bcba6a98736f2f480114922186f6ca458))

## v2.2.3 (2024-06-04)

### Fix

* S8 maxv has a wash and fill dock ([#213](https://github.com/humbertogontijo/python-roborock/issues/213)) ([`018fd05`](https://github.com/humbertogontijo/python-roborock/commit/018fd052360dffd238919e336943809720457c4e))

## v2.2.2 (2024-05-16)

### Fix

* Handle weird clean record response ([#206](https://github.com/humbertogontijo/python-roborock/issues/206)) ([`07ce71a`](https://github.com/humbertogontijo/python-roborock/commit/07ce71a2cd8085136952bd7639f6f4a2e273faf9))

## v2.2.1 (2024-05-11)

### Fix
Expand Down
6 changes: 4 additions & 2 deletions docs/source/api_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,9 @@ reset_consumable

Description:

Parameters:
Parameters: List of consumables to reset. For example, to reset consumables 'strainer_work_times' and 'sensor_dirty_time' the parameter would be

['strainer_work_times', 'sensor_dirty_time']

====================== =========
Vacuum Model Supported
Expand Down Expand Up @@ -1247,7 +1249,7 @@ load_multi_map

Description:

Parameters: ???
Parameters: number (the floor/map index)

..
Need to work out parameter format
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "python-roborock"
version = "2.2.1"
version = "2.5.0"
description = "A package to control Roborock vacuums."
authors = ["humbertogontijo <humbertogontijo@users.noreply.github.com>"]
license = "GPL-3.0-only"
Expand Down
104 changes: 102 additions & 2 deletions roborock/code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from enum import Enum, IntEnum

_LOGGER = logging.getLogger(__name__)
completed_warnings = set()


class RoborockEnum(IntEnum):
Expand All @@ -16,10 +17,16 @@ def name(self) -> str:
@classmethod
def _missing_(cls: type[RoborockEnum], key) -> RoborockEnum:
if hasattr(cls, "unknown"):
_LOGGER.warning(f"Missing {cls.__name__} code: {key} - defaulting to 'unknown'")
warning = f"Missing {cls.__name__} code: {key} - defaulting to 'unknown'"
if warning not in completed_warnings:
completed_warnings.add(warning)
_LOGGER.warning(warning)
return cls.unknown # type: ignore
default_value = next(item for item in cls)
_LOGGER.warning(f"Missing {cls.__name__} code: {key} - defaulting to {default_value}")
warning = f"Missing {cls.__name__} code: {key} - defaulting to {default_value}"
if warning not in completed_warnings:
completed_warnings.add(warning)
_LOGGER.warning(warning)
return default_value

@classmethod
Expand Down Expand Up @@ -248,7 +255,10 @@ class RoborockFanSpeedP10(RoborockFanPowerCode):

class RoborockFanSpeedS8MaxVUltra(RoborockFanPowerCode):
off = 105
quiet = 101
balanced = 102
turbo = 103
max = 104
custom = 106
max_plus = 108
smart_mode = 110
Expand Down Expand Up @@ -324,6 +334,8 @@ class RoborockMopIntensityS8MaxVUltra(RoborockMopIntensityCode):
low = 201
medium = 202
high = 203
custom = 204
max = 208
smart_mode = 209
custom_water_flow = 207

Expand Down Expand Up @@ -394,6 +406,7 @@ class RoborockDockWashTowelModeCode(RoborockEnum):
light = 0
balanced = 1
deep = 2
smart = 10


class RoborockCategory(Enum):
Expand All @@ -409,6 +422,93 @@ def __missing__(self, key):
return RoborockCategory.UNKNOWN


class RoborockFinishReason(RoborockEnum):
manual_interrupt = 21 # Cleaning interrupted by user
cleanup_interrupted = 24 # Cleanup interrupted
manual_interrupt_2 = 21
breakpoint = 32 # Could not continue cleaning
breakpoint_2 = 33
cleanup_interrupted_2 = 34
manual_interrupt_3 = 35
manual_interrupt_4 = 36
manual_interrupt_5 = 37
manual_interrupt_6 = 43
locate_fail = 45 # Positioning Failed
cleanup_interrupted_3 = 64
locate_fail_2 = 65
manual_interrupt_7 = 48
manual_interrupt_8 = 49
manual_interrupt_9 = 50
cleanup_interrupted_4 = 51
finished_cleaning = 52 # Finished cleaning
finished_cleaning_2 = 54
finished_cleaning_3 = 55
finished_cleaning_4 = 56
finished_clenaing_5 = 57
manual_interrupt_10 = 60
area_unreachable = 61 # Area unreachable
area_unreachable_2 = 62
washing_error = 67 # Washing error
back_to_wash_failure = 68 # Failed to return to the dock
cleanup_interrupted_5 = 101
breakpoint_4 = 102
manual_interrupt_11 = 103
cleanup_interrupted_6 = 104
cleanup_interrupted_7 = 105
cleanup_interrupted_8 = 106
cleanup_interrupted_9 = 107
cleanup_interrupted_10 = 109
cleanup_interrupted_11 = 110
patrol_success = 114 # Cruise completed
patrol_fail = 115 # Cruise failed
pet_patrol_success = 116 # Pet found
pet_patrol_fail = 117 # Pet found failed


class RoborockInCleaning(RoborockEnum):
complete = 0
global_clean_not_complete = 1
zone_clean_not_complete = 2
segment_clean_not_complete = 3


class RoborockCleanType(RoborockEnum):
all_zone = 1
draw_zone = 2
select_zone = 3
quick_build = 4
video_patrol = 5
pet_patrol = 6


class RoborockStartType(RoborockEnum):
button = 1
app = 2
schedule = 3
mi_home = 4
quick_start = 5
voice_control = 13
routines = 101
alexa = 801
google = 802
ifttt = 803
yandex = 804
homekit = 805
xiaoai = 806
tmall_genie = 807
duer = 808
dingdong = 809
siri = 810
clova = 811
wechat = 901
alipay = 902
aqara = 903
hisense = 904
huawei = 905
widget_launch = 820
smart_watch = 821


class DyadSelfCleanMode(RoborockEnum):
self_clean = 1
self_clean_and_dry = 2
Expand Down
5 changes: 4 additions & 1 deletion roborock/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@
ROBOROCK_C1 = "roborock.vacuum.c1"
ROBOROCK_S8_PRO_ULTRA = "roborock.vacuum.a70"
ROBOROCK_S8 = "roborock.vacuum.a51"
ROBOROCK_P10 = "roborock.vacuum.a75"
ROBOROCK_P10 = "roborock.vacuum.a75" # also known as q_revo
ROBOROCK_S8_MAXV_ULTRA = "roborock.vacuum.a97"
ROBOROCK_QREVO_S = "roborock.vacuum.a104"
ROBOROCK_QREVO_PRO = "roborock.vacuum.a101"
ROBOROCK_QREVO_MAXV = "roborock.vacuum.a87"

ROBOROCK_DYAD_AIR = "roborock.wetdryvac.a107"
ROBOROCK_DYAD_PRO_COMBO = "roborock.wetdryvac.a83"
Expand Down
21 changes: 17 additions & 4 deletions roborock/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from .code_mappings import (
RoborockCategory,
RoborockCleanType,
RoborockDockDustCollectionModeCode,
RoborockDockErrorCode,
RoborockDockTypeCode,
Expand All @@ -25,6 +26,8 @@
RoborockFanSpeedS7,
RoborockFanSpeedS7MaxV,
RoborockFanSpeedS8MaxVUltra,
RoborockFinishReason,
RoborockInCleaning,
RoborockMopIntensityCode,
RoborockMopIntensityP10,
RoborockMopIntensityS5Max,
Expand All @@ -36,6 +39,7 @@
RoborockMopModeS7,
RoborockMopModeS8MaxVUltra,
RoborockMopModeS8ProUltra,
RoborockStartType,
RoborockStateCode,
)
from .const import (
Expand All @@ -47,6 +51,9 @@
ROBOROCK_G10S_PRO,
ROBOROCK_P10,
ROBOROCK_Q7_MAX,
ROBOROCK_QREVO_MAXV,
ROBOROCK_QREVO_PRO,
ROBOROCK_QREVO_S,
ROBOROCK_S4_MAX,
ROBOROCK_S5_MAX,
ROBOROCK_S6,
Expand Down Expand Up @@ -408,7 +415,7 @@ class Status(RoborockBase):
square_meter_clean_area: float | None = None
error_code: RoborockErrorCode | None = None
map_present: int | None = None
in_cleaning: int | None = None
in_cleaning: RoborockInCleaning | None = None
in_returning: int | None = None
in_fresh_state: int | None = None
lab_status: int | None = None
Expand Down Expand Up @@ -574,6 +581,12 @@ class S8MaxvUltraStatus(Status):
ROBOROCK_S8_PRO_ULTRA: S8ProUltraStatus,
ROBOROCK_G10S_PRO: S7MaxVStatus,
ROBOROCK_P10: P10Status,
# These likely are not correct,
# but i am currently unable to do my typical reverse engineering/ get any data from users on this,
# so this will be here in the mean time.
ROBOROCK_QREVO_S: P10Status,
ROBOROCK_QREVO_MAXV: P10Status,
ROBOROCK_QREVO_PRO: P10Status,
ROBOROCK_S8_MAXV_ULTRA: S8MaxvUltraStatus,
}

Expand Down Expand Up @@ -613,9 +626,9 @@ class CleanRecord(RoborockBase):
square_meter_area: float | None = None
error: int | None = None
complete: int | None = None
start_type: int | None = None
clean_type: int | None = None
finish_reason: int | None = None
start_type: RoborockStartType | None = None
clean_type: RoborockCleanType | None = None
finish_reason: RoborockFinishReason | None = None
dust_collection_status: int | None = None
avoid_count: int | None = None
wash_count: int | None = None
Expand Down
2 changes: 1 addition & 1 deletion roborock/local_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ async def hello(self):
except Exception as e:
self._logger.error(e)

async def ping(self):
async def ping(self) -> None:
request_id = 2
protocol = RoborockMessageProtocol.PING_REQUEST
return await self.send_message(
Expand Down
27 changes: 23 additions & 4 deletions roborock/version_1_apis/roborock_client_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
RoborockDockTypeCode.empty_wash_fill_dock,
RoborockDockTypeCode.s8_dock,
RoborockDockTypeCode.p10_dock,
RoborockDockTypeCode.s8_maxv_ultra_dock,
]
RT = TypeVar("RT", bound=RoborockBase)
EVICT_TIME = 60
Expand Down Expand Up @@ -104,7 +105,7 @@ async def async_value(self):
def stop(self):
self.task.cancel()

async def update_value(self, params):
async def update_value(self, params) -> None:
if self.attribute.set_command is None:
raise RoborockException(f"{self.attribute.attribute} have no set command")
response = await self.api._send_command(self.attribute.set_command, params)
Expand All @@ -118,7 +119,7 @@ async def add_value(self, params):
await self._async_value()
return response

async def close_value(self, params=None):
async def close_value(self, params=None) -> None:
if self.attribute.close_command is None:
raise RoborockException(f"{self.attribute.attribute} have no close command")
response = await self.api._send_command(self.attribute.close_command, params)
Expand Down Expand Up @@ -153,7 +154,7 @@ def release(self):
super().release()
[item.stop() for item in self.cache.values()]

async def async_release(self):
async def async_release(self) -> None:
await super().async_release()
[item.stop() for item in self.cache.values()]

Expand Down Expand Up @@ -197,6 +198,24 @@ async def get_clean_record(self, record_id: int) -> CleanRecord | None:
if isinstance(record, dict):
return CleanRecord.from_dict(record)
elif isinstance(record, list):
if isinstance(record[-1], dict):
records = [CleanRecord.from_dict(rec) for rec in record]
final_record = records[-1]
try:
# This code is semi-presumptions - so it is put in a try finally to be safe.
final_record.begin = records[0].begin
final_record.begin_datetime = records[0].begin_datetime
final_record.start_type = records[0].start_type
for rec in records[0:-1]:
final_record.duration += rec.duration if rec.duration is not None else 0
final_record.area += rec.area if rec.area is not None else 0
final_record.avoid_count += rec.avoid_count if rec.avoid_count is not None else 0
final_record.wash_count += rec.wash_count if rec.wash_count is not None else 0
final_record.square_meter_area += (
rec.square_meter_area if rec.square_meter_area is not None else 0
)
finally:
return final_record
# There are still a few unknown variables in this.
begin, end, duration, area = unpack_list(record, 4)
return CleanRecord(begin=begin, end=end, duration=duration, area=area)
Expand Down Expand Up @@ -277,7 +296,7 @@ async def get_room_mapping(self) -> list[RoomMapping] | None:
"""Gets the mapping from segment id -> iot id. Only works on local api."""
mapping: list = await self.send_command(RoborockCommand.GET_ROOM_MAPPING)
if isinstance(mapping, list):
if not isinstance(mapping[0], list) and len(mapping) == 2:
if len(mapping) == 2 and not isinstance(mapping[0], list):
return [RoomMapping(segment_id=mapping[0], iot_id=mapping[1])]
return [
RoomMapping(segment_id=segment_id, iot_id=iot_id) # type: ignore
Expand Down
Loading