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

Add XML command "CleanArea" #817

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
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
27 changes: 27 additions & 0 deletions deebot_client/commands/xml/clean.py
Original file line number Diff line number Diff line change
@@ -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:
# <ctl><clean type='SpotArea' act='s' speed='standard' deep='1' mid='4,5'/></ctl>

super().__init__(
{
"type": mode.xml_value,
"act": CleanAction.START.xml_value,
"speed": "standard", # TODO: FanSpeedLevel.NORMAL.xml_value, after #560 is merged
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this mean that the bot will always start with the fan level at standard and not the one defined in the fan speed entity?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it makes sense to pass it as parameter. But we have to wait for #560 for the new FanSpeedLevel Enum.

We can either merge it like it is and address this in another PR, or we wait for #560

"deep": str(cleanings),
"mid": area,
}
)
22 changes: 12 additions & 10 deletions deebot_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
29 changes: 29 additions & 0 deletions deebot_client/util/enum.py
Original file line number Diff line number Diff line change
@@ -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:
flubshi marked this conversation as resolved.
Show resolved Hide resolved
"""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)
24 changes: 24 additions & 0 deletions tests/commands/xml/test_clean.py
Original file line number Diff line number Diff line change
@@ -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("<ctl ret='ok'/>")
await assert_command(
command, json, None, command_result=CommandResult(command_result)
)
35 changes: 35 additions & 0 deletions tests/util/test_enum.py
Original file line number Diff line number Diff line change
@@ -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
Loading