From 4e3f527288b6bb7a29d4187f40ca8b5933d1f7c4 Mon Sep 17 00:00:00 2001 From: Giovanni Condello Date: Tue, 29 Apr 2025 09:14:34 +0200 Subject: [PATCH 1/5] Add definition for 2pv572 --- deebot_client/capabilities.py | 6 +- deebot_client/commands/xml/map.py | 7 +- deebot_client/hardware/deebot/2pv572.py | 126 ++++++++++++++++++++++++ tests/commands/xml/test_map.py | 4 +- 4 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 deebot_client/hardware/deebot/2pv572.py diff --git a/deebot_client/capabilities.py b/deebot_client/capabilities.py index 1181ed45d..b8e792701 100644 --- a/deebot_client/capabilities.py +++ b/deebot_client/capabilities.py @@ -175,9 +175,9 @@ class CapabilityMap: clear: CapabilityExecute[[]] | None = None major: CapabilityEvent[MajorMapEvent] minor: CapabilityExecute[[str, int]] - multi_state: CapabilitySetEnable[MultimapStateEvent] + multi_state: CapabilitySetEnable[MultimapStateEvent] | None = None position: CapabilityEvent[PositionsEvent] - relocation: CapabilityExecute[[]] + relocation: CapabilityExecute[[]] | None = None rooms: CapabilityEvent[RoomsEvent] trace: CapabilityEvent[MapTraceEvent] @@ -213,7 +213,7 @@ class CapabilitySettings: sweep_mode: CapabilitySetEnable[SweepModeEvent] | None = None true_detect: CapabilitySetEnable[TrueDetectEvent] | None = None voice_assistant: CapabilitySetEnable[VoiceAssistantStateEvent] | None = None - volume: CapabilitySet[VolumeEvent, [int]] + volume: CapabilitySet[VolumeEvent, [int]] | None = None @dataclass(frozen=True, kw_only=True) diff --git a/deebot_client/commands/xml/map.py b/deebot_client/commands/xml/map.py index d2dec98eb..e04d797fa 100644 --- a/deebot_client/commands/xml/map.py +++ b/deebot_client/commands/xml/map.py @@ -239,7 +239,12 @@ class PullMP(XmlCommandWithMessageHandling): NAME = "PullMP" - def __init__(self, *, piece_index: int) -> None: + def __init__( + self, + map_id: str, # pylint: disable=unused-argument # noqa: ARG002 + piece_index: int, + ) -> None: + """Declare the map_id argument not to break compatibility with CapabilityMap.""" self._piece_index = piece_index super().__init__({"pid": str(piece_index)}) diff --git a/deebot_client/hardware/deebot/2pv572.py b/deebot_client/hardware/deebot/2pv572.py new file mode 100644 index 000000000..2e53c6c6f --- /dev/null +++ b/deebot_client/hardware/deebot/2pv572.py @@ -0,0 +1,126 @@ +"""2pv572 Capabilities.""" + +from __future__ import annotations + +from deebot_client.capabilities import ( + Capabilities, + CapabilityClean, + CapabilityCleanAction, + CapabilityCustomCommand, + CapabilityEvent, + CapabilityExecute, + CapabilityLifeSpan, + CapabilityMap, + CapabilitySettings, + CapabilitySetTypes, + CapabilityStats, + DeviceType, +) +from deebot_client.commands.json import GetNetInfoLegacy +from deebot_client.commands.json.custom import CustomCommand +from deebot_client.commands.xml import ( + Charge, + Clean, + CleanArea, + GetBatteryInfo, + GetChargerPos, + GetCleanLogs, + GetCleanSpeed, + GetCleanState, + GetError, + GetLifeSpan, + GetMapM, + GetMapSt, + GetPos, + GetTrM, + PlaySound, + PullMP, + ResetLifeSpan, + SetCleanSpeed, +) +from deebot_client.commands.xml.charge_state import GetChargeState +from deebot_client.commands.xml.stats import GetCleanSum +from deebot_client.const import DataType +from deebot_client.events import ( + AvailabilityEvent, + BatteryEvent, + CleanLogEvent, + CustomCommandEvent, + ErrorEvent, + FanSpeedEvent, + FanSpeedLevel, + LifeSpan, + LifeSpanEvent, + NetworkInfoEvent, + ReportStatsEvent, + RoomsEvent, + StateEvent, + StatsEvent, + TotalStatsEvent, +) +from deebot_client.events.map import ( + CachedMapInfoEvent, + MajorMapEvent, + MapChangedEvent, + MapTraceEvent, + PositionsEvent, +) +from deebot_client.models import StaticDeviceInfo +from deebot_client.util import short_name + +from . import DEVICES + +DEVICES[short_name(__name__)] = StaticDeviceInfo( + DataType.XML, + Capabilities( + availability=CapabilityEvent(AvailabilityEvent, []), + battery=CapabilityEvent(BatteryEvent, [GetBatteryInfo()]), + charge=CapabilityExecute(Charge), + clean=CapabilityClean( + action=CapabilityCleanAction(command=Clean, area=CleanArea), + log=CapabilityEvent(CleanLogEvent, [GetCleanLogs()]), + ), + custom=CapabilityCustomCommand( + event=CustomCommandEvent, get=[], set=CustomCommand + ), + device_type=DeviceType.VACUUM, + error=CapabilityEvent(ErrorEvent, [GetError()]), + fan_speed=CapabilitySetTypes( + event=FanSpeedEvent, + get=[GetCleanSpeed()], + set=SetCleanSpeed, + types=( + FanSpeedLevel.NORMAL, + FanSpeedLevel.MAX, + ), + ), + life_span=CapabilityLifeSpan( + types=(LifeSpan.BRUSH, LifeSpan.SIDE_BRUSH, LifeSpan.DUST_CASE_HEAP), + event=LifeSpanEvent, + get=[ + GetLifeSpan(LifeSpan.BRUSH), + GetLifeSpan(LifeSpan.SIDE_BRUSH), + GetLifeSpan(LifeSpan.DUST_CASE_HEAP), + ], + reset=ResetLifeSpan, + ), + map=CapabilityMap( + cached_info=CapabilityEvent(CachedMapInfoEvent, [GetMapSt()]), + changed=CapabilityEvent(MapChangedEvent, []), + major=CapabilityEvent(MajorMapEvent, [GetMapM()]), + minor=CapabilityExecute(PullMP), + position=CapabilityEvent(PositionsEvent, [GetPos(), GetChargerPos()]), + rooms=CapabilityEvent(RoomsEvent, [GetMapSt()]), + trace=CapabilityEvent(MapTraceEvent, [GetTrM()]), + ), + network=CapabilityEvent(NetworkInfoEvent, [GetNetInfoLegacy()]), + play_sound=CapabilityExecute(PlaySound), + state=CapabilityEvent(StateEvent, [GetChargeState(), GetCleanState()]), + stats=CapabilityStats( + clean=CapabilityEvent(StatsEvent, []), + report=CapabilityEvent(ReportStatsEvent, []), + total=CapabilityEvent(TotalStatsEvent, [GetCleanSum()]), + ), + settings=CapabilitySettings(), + ), +) diff --git a/tests/commands/xml/test_map.py b/tests/commands/xml/test_map.py index 49a8a3d59..43a78c6fe 100644 --- a/tests/commands/xml/test_map.py +++ b/tests/commands/xml/test_map.py @@ -219,7 +219,7 @@ async def test_PullM_error(xml: str) -> None: async def test_PullMP(xml: str, expected_event: MinorMapEvent) -> None: json = get_request_xml(xml) await assert_command( - PullMP(piece_index=1), + PullMP(map_id="unused", piece_index=1), json, expected_event, command_result=CommandResult( @@ -239,7 +239,7 @@ async def test_PullMP(xml: str, expected_event: MinorMapEvent) -> None: async def test_PullMP_error(xml: str) -> None: json = get_request_xml(xml) await assert_command( - PullMP(piece_index=1), + PullMP(map_id="unused", piece_index=1), json, None, command_result=CommandResult(HandlingState.ANALYSE_LOGGED), From 199084bb8a292a53ebce67f722ae5d69fa90f20a Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Tue, 29 Apr 2025 08:30:59 +0000 Subject: [PATCH 2/5] refactor --- deebot_client/capabilities.py | 2 +- deebot_client/commands/json/map.py | 2 +- deebot_client/commands/xml/map.py | 3 +-- deebot_client/map.py | 2 +- tests/commands/xml/test_map.py | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/deebot_client/capabilities.py b/deebot_client/capabilities.py index b8e792701..3fb27b5c9 100644 --- a/deebot_client/capabilities.py +++ b/deebot_client/capabilities.py @@ -174,7 +174,7 @@ class CapabilityMap: changed: CapabilityEvent[MapChangedEvent] clear: CapabilityExecute[[]] | None = None major: CapabilityEvent[MajorMapEvent] - minor: CapabilityExecute[[str, int]] + minor: CapabilityExecute[[int, str]] multi_state: CapabilitySetEnable[MultimapStateEvent] | None = None position: CapabilityEvent[PositionsEvent] relocation: CapabilityExecute[[]] | None = None diff --git a/deebot_client/commands/json/map.py b/deebot_client/commands/json/map.py index 8aba84596..92f98a518 100644 --- a/deebot_client/commands/json/map.py +++ b/deebot_client/commands/json/map.py @@ -389,7 +389,7 @@ class GetMinorMap(JsonCommandWithMessageHandling, MessageBodyDataDict): NAME = "getMinorMap" - def __init__(self, map_id: str, piece_index: int) -> None: + def __init__(self, piece_index: int, map_id: str) -> None: super().__init__({"mid": map_id, "type": "ol", "pieceIndex": piece_index}) @classmethod diff --git a/deebot_client/commands/xml/map.py b/deebot_client/commands/xml/map.py index e04d797fa..19af53232 100644 --- a/deebot_client/commands/xml/map.py +++ b/deebot_client/commands/xml/map.py @@ -241,10 +241,9 @@ class PullMP(XmlCommandWithMessageHandling): def __init__( self, - map_id: str, # pylint: disable=unused-argument # noqa: ARG002 piece_index: int, + _: str | None = None, ) -> None: - """Declare the map_id argument not to break compatibility with CapabilityMap.""" self._piece_index = piece_index super().__init__({"pid": str(piece_index)}) diff --git a/deebot_client/map.py b/deebot_client/map.py index 52dad3a26..54f6dc00b 100644 --- a/deebot_client/map.py +++ b/deebot_client/map.py @@ -105,7 +105,7 @@ async def on_major_map(event: MajorMapEvent) -> None: ): tg.create_task( self._execute_command( - self._capabilities.minor.execute(event.map_id, idx) + self._capabilities.minor.execute(idx, event.map_id) ) ) diff --git a/tests/commands/xml/test_map.py b/tests/commands/xml/test_map.py index 43a78c6fe..49a8a3d59 100644 --- a/tests/commands/xml/test_map.py +++ b/tests/commands/xml/test_map.py @@ -219,7 +219,7 @@ async def test_PullM_error(xml: str) -> None: async def test_PullMP(xml: str, expected_event: MinorMapEvent) -> None: json = get_request_xml(xml) await assert_command( - PullMP(map_id="unused", piece_index=1), + PullMP(piece_index=1), json, expected_event, command_result=CommandResult( @@ -239,7 +239,7 @@ async def test_PullMP(xml: str, expected_event: MinorMapEvent) -> None: async def test_PullMP_error(xml: str) -> None: json = get_request_xml(xml) await assert_command( - PullMP(map_id="unused", piece_index=1), + PullMP(piece_index=1), json, None, command_result=CommandResult(HandlingState.ANALYSE_LOGGED), From ea768249bd55d782bbefb42bc4351cc09719e386 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Tue, 29 Apr 2025 08:33:31 +0000 Subject: [PATCH 3/5] format --- deebot_client/commands/xml/map.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/deebot_client/commands/xml/map.py b/deebot_client/commands/xml/map.py index 19af53232..fbcfd84af 100644 --- a/deebot_client/commands/xml/map.py +++ b/deebot_client/commands/xml/map.py @@ -239,11 +239,7 @@ class PullMP(XmlCommandWithMessageHandling): NAME = "PullMP" - def __init__( - self, - piece_index: int, - _: str | None = None, - ) -> None: + def __init__(self, piece_index: int, _: str | None = None) -> None: self._piece_index = piece_index super().__init__({"pid": str(piece_index)}) From c625f55c9dd84c7a94a10825db60823ebb3e209e Mon Sep 17 00:00:00 2001 From: Giovanni Condello Date: Tue, 29 Apr 2025 15:08:27 +0200 Subject: [PATCH 4/5] Add ls1ok3 as an alias of 2pv572 since we have no mopping support yet --- deebot_client/hardware/deebot/ls1ok3.py | 1 + 1 file changed, 1 insertion(+) create mode 120000 deebot_client/hardware/deebot/ls1ok3.py diff --git a/deebot_client/hardware/deebot/ls1ok3.py b/deebot_client/hardware/deebot/ls1ok3.py new file mode 120000 index 000000000..8d0c1f3ec --- /dev/null +++ b/deebot_client/hardware/deebot/ls1ok3.py @@ -0,0 +1 @@ +2pv572.py \ No newline at end of file From 7b51a86a5e6eeb6152e665801db269058d68d09b Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Wed, 30 Apr 2025 09:16:18 +0000 Subject: [PATCH 5/5] Use model name --- deebot_client/hardware/deebot/2pv572.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deebot_client/hardware/deebot/2pv572.py b/deebot_client/hardware/deebot/2pv572.py index 2e53c6c6f..c54c0eb94 100644 --- a/deebot_client/hardware/deebot/2pv572.py +++ b/deebot_client/hardware/deebot/2pv572.py @@ -1,4 +1,4 @@ -"""2pv572 Capabilities.""" +"""OZMO 905 Capabilities.""" from __future__ import annotations