diff --git a/deebot_client/commands/xml/clean.py b/deebot_client/commands/xml/clean.py new file mode 100644 index 00000000..d6138efb --- /dev/null +++ b/deebot_client/commands/xml/clean.py @@ -0,0 +1,27 @@ +"""Clean commands.""" + +from __future__ import annotations + +from deebot_client.models import CleanAction, CleanMode + +from .common import ExecuteCommand + + +class CleanArea(ExecuteCommand): + """Clean area command.""" + + NAME = "Clean" + HAS_SUB_ELEMENT = True + + def __init__(self, mode: CleanMode, area: str, cleanings: int = 1) -> None: + # + + super().__init__( + { + "type": mode.xml_value, + "act": CleanAction.START.xml_value, + "speed": "standard", # TODO: FanSpeedLevel.NORMAL.xml_value, after #560 is merged + "deep": str(cleanings), + "mid": area, + } + ) diff --git a/deebot_client/models.py b/deebot_client/models.py index c88ccabb..426dccf6 100644 --- a/deebot_client/models.py +++ b/deebot_client/models.py @@ -3,10 +3,12 @@ from __future__ import annotations from dataclasses import dataclass -from enum import IntEnum, StrEnum, unique +from enum import IntEnum, unique from pathlib import Path from typing import TYPE_CHECKING, Required, TypedDict +from deebot_client.util.enum import StrEnumWithXml + if TYPE_CHECKING: from deebot_client.capabilities import Capabilities from deebot_client.const import DataType @@ -64,22 +66,22 @@ class State(IntEnum): @unique -class CleanAction(StrEnum): +class CleanAction(StrEnumWithXml): """Enum class for all possible clean actions.""" - START = "start" - PAUSE = "pause" - RESUME = "resume" - STOP = "stop" + START = "start", "s" + PAUSE = "pause", "p" + RESUME = "resume", "r" + STOP = "stop", "h" @unique -class CleanMode(StrEnum): +class CleanMode(StrEnumWithXml): """Enum class for all possible clean modes.""" - AUTO = "auto" - SPOT_AREA = "spotArea" - CUSTOM_AREA = "customArea" + AUTO = "auto", "auto" + SPOT_AREA = "spotArea", "SpotArea" + CUSTOM_AREA = "customArea", "spot" @dataclass(frozen=True) diff --git a/deebot_client/util/enum.py b/deebot_client/util/enum.py new file mode 100644 index 00000000..c6036fa0 --- /dev/null +++ b/deebot_client/util/enum.py @@ -0,0 +1,29 @@ +"""Enum util.""" + +from __future__ import annotations + +from enum import StrEnum +from typing import Self + + +class StrEnumWithXml(StrEnum): + """String enum with xml value.""" + + xml_value: str + + def __new__(cls, value: str, xml_value: str = "") -> Self: + """Create new StrEnumWithXml.""" + obj = str.__new__(cls, value) + obj._value_ = value + obj.xml_value = xml_value + return obj + + @classmethod + def from_xml(cls, value: str) -> Self: + """Get CleanAction from xml value.""" + for member in cls: + if member.xml_value == value: + return member + + msg = f"{value} is not a valid {cls.__name__}" + raise ValueError(msg) diff --git a/tests/commands/xml/test_clean.py b/tests/commands/xml/test_clean.py new file mode 100644 index 00000000..b67c8ee6 --- /dev/null +++ b/tests/commands/xml/test_clean.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +import pytest + +from deebot_client.command import CommandResult +from deebot_client.commands.xml.clean import CleanArea +from deebot_client.message import HandlingState +from deebot_client.models import CleanMode +from tests.commands import assert_command + +from . import get_request_xml + + +@pytest.mark.parametrize( + ("command", "command_result"), + [ + (CleanArea(CleanMode.SPOT_AREA, "4", 1), HandlingState.SUCCESS), + ], +) +async def test_CleanArea(command: CleanArea, command_result: HandlingState) -> None: + json = get_request_xml("") + await assert_command( + command, json, None, command_result=CommandResult(command_result) + ) diff --git a/tests/util/test_enum.py b/tests/util/test_enum.py new file mode 100644 index 00000000..8b34fe47 --- /dev/null +++ b/tests/util/test_enum.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import pytest + +from deebot_client.util.enum import StrEnumWithXml + + +class TestStrEnumWithXml(StrEnumWithXml): + """Simple Enum for testing.""" + + ENUM1 = "value1", "xmlvalue1" + ENUM2 = "value2", "xmlvalue2" + + +@pytest.mark.parametrize( + ("test_enum", "test_value", "test_xml_value"), + [ + (TestStrEnumWithXml.ENUM1, "value1", "xmlvalue1"), + (TestStrEnumWithXml.ENUM2, "value2", "xmlvalue2"), + ], +) +def test_StrEnumWithXml_values( + test_enum: TestStrEnumWithXml, test_value: str, test_xml_value: str +) -> None: + assert test_enum.value == test_value + assert test_enum.xml_value == test_xml_value + assert test_value == TestStrEnumWithXml.from_xml(test_xml_value).value + assert test_xml_value == TestStrEnumWithXml(test_value).xml_value + + # test ENUM from invalid xml + try: + TestStrEnumWithXml.from_xml("this_is_invalid") + raise AssertionError + except ValueError: + assert True