From 136fb4d51020e3d19a9424a4e037b5d14f43ac75 Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Mon, 26 Aug 2024 22:17:20 +0200
Subject: [PATCH 01/12] Add XML command "SetFanSpeed"
---
deebot_client/commands/xml/__init__.py | 3 ++-
deebot_client/commands/xml/common.py | 29 ++++++++++++++++++++++---
deebot_client/commands/xml/fan_speed.py | 18 ++++++++++++++-
tests/commands/xml/test_fan_speed.py | 12 ++++++++++
4 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/deebot_client/commands/xml/__init__.py b/deebot_client/commands/xml/__init__.py
index 7448f8097..de7952385 100644
--- a/deebot_client/commands/xml/__init__.py
+++ b/deebot_client/commands/xml/__init__.py
@@ -8,7 +8,7 @@
from .charge_state import GetChargeState
from .error import GetError
-from .fan_speed import GetFanSpeed
+from .fan_speed import GetFanSpeed, SetFanSpeed
from .pos import GetPos
if TYPE_CHECKING:
@@ -18,6 +18,7 @@
"GetChargeState",
"GetError",
"GetFanSpeed",
+ "SetFanSpeed",
"GetPos",
]
diff --git a/deebot_client/commands/xml/common.py b/deebot_client/commands/xml/common.py
index d63e40449..e477509dc 100644
--- a/deebot_client/commands/xml/common.py
+++ b/deebot_client/commands/xml/common.py
@@ -8,10 +8,10 @@
from defusedxml import ElementTree # type: ignore[import-untyped]
-from deebot_client.command import Command, CommandWithMessageHandling
+from deebot_client.command import Command, CommandWithMessageHandling, SetCommand
from deebot_client.const import DataType
from deebot_client.logging_filter import get_logger
-from deebot_client.message import HandlingResult, MessageStr
+from deebot_client.message import HandlingResult, MessageStr, HandlingState
if TYPE_CHECKING:
from deebot_client.event_bus import EventBus
@@ -25,7 +25,6 @@ class XmlCommand(Command):
data_type: DataType = DataType.XML
@property # type: ignore[misc]
- @classmethod
def has_sub_element(cls) -> bool:
"""Return True if command has inner element."""
return False
@@ -65,3 +64,27 @@ def _handle_str(cls, event_bus: EventBus, message: str) -> HandlingResult:
"""
xml = ElementTree.fromstring(message)
return cls._handle_xml(event_bus, xml)
+
+
+class ExecuteCommand(XmlCommandWithMessageHandling, ABC):
+ """Command, which is executing something (ex. Charge)."""
+
+ @classmethod
+ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
+ """Handle message->xml and notify the correct event subscribers.
+
+ :return: A message response
+ """
+ # Success event looks like
+ if xml.attrib.get("ret") == "ok":
+ return HandlingResult.success()
+
+ _LOGGER.warning('Command "%s" was not successful. XML response: %s', cls.name, xml)
+ return HandlingResult(HandlingState.FAILED)
+
+
+class XmlSetCommand(ExecuteCommand, SetCommand, ABC):
+ """Xml base set command.
+
+ Command needs to be linked to the "get" command, for handling (updating) the sensors.
+ """
\ No newline at end of file
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index d0c1f3792..649d20d01 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -2,12 +2,14 @@
from __future__ import annotations
+from types import MappingProxyType
from typing import TYPE_CHECKING
+from deebot_client.command import InitParam
from deebot_client.events import FanSpeedEvent, FanSpeedLevel
from deebot_client.message import HandlingResult
-from .common import XmlCommandWithMessageHandling
+from .common import XmlCommandWithMessageHandling, XmlSetCommand
if TYPE_CHECKING:
from xml.etree.ElementTree import Element
@@ -42,3 +44,17 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
return HandlingResult.success()
return HandlingResult.analyse()
+
+
+class SetFanSpeed(XmlSetCommand):
+ """Set fan speed command."""
+
+ name = "SetCleanSpeed"
+ get_command = GetFanSpeed
+ _mqtt_params = MappingProxyType({"speed": InitParam(FanSpeedLevel)})
+
+ def __init__(self, speed: FanSpeedLevel | str) -> None:
+ if isinstance(speed, int):
+ speed = "strong" if speed in [1, 2] else "normal"
+ super().__init__({"speed": speed})
+
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index bf728ecfc..740091fce 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -6,11 +6,13 @@
from deebot_client.command import CommandResult
from deebot_client.commands.xml import GetFanSpeed
+from deebot_client.commands.xml.fan_speed import SetFanSpeed
from deebot_client.events import FanSpeedEvent, FanSpeedLevel
from deebot_client.message import HandlingState
from tests.commands import assert_command
from . import get_request_xml
+from ..json import assert_set_command
if TYPE_CHECKING:
from deebot_client.events.base import Event
@@ -42,3 +44,13 @@ async def test_get_fan_speed_error(xml: str) -> None:
None,
command_result=CommandResult(HandlingState.ANALYSE_LOGGED),
)
+
+async def test_set_fan_speed() -> None:
+ command = SetFanSpeed(FanSpeedLevel.MAX)
+ json = get_request_xml("")
+ await assert_command(command, json, None, command_result=CommandResult(HandlingState.SUCCESS))
+
+async def test_set_fan_speed_error() -> None:
+ command = SetFanSpeed("invalid")
+ json = get_request_xml("")
+ await assert_command(command, json, None, command_result=CommandResult(HandlingState.FAILED))
From 315307a97700f89f9ee649a87afc42474e68f848 Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Wed, 11 Sep 2024 19:27:02 +0200
Subject: [PATCH 02/12] Add feedback for Get/SetCleanSpeed commands
---
deebot_client/commands/xml/__init__.py | 8 +++++---
deebot_client/commands/xml/fan_speed.py | 18 +++++++++---------
deebot_client/events/fan_speed.py | 2 ++
tests/commands/xml/test_fan_speed.py | 18 +++++++++---------
4 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/deebot_client/commands/xml/__init__.py b/deebot_client/commands/xml/__init__.py
index 4541cba55..ba5c85e47 100644
--- a/deebot_client/commands/xml/__init__.py
+++ b/deebot_client/commands/xml/__init__.py
@@ -8,7 +8,7 @@
from .charge_state import GetChargeState
from .error import GetError
-from .fan_speed import GetFanSpeed, SetFanSpeed
+from .fan_speed import GetCleanSpeed, SetCleanSpeed
from .pos import GetPos
from .stats import GetCleanSum
@@ -16,17 +16,19 @@
from .common import XmlCommand
__all__ = [
+ "GetCleanSpeed",
+ "SetCleanSpeed",
"GetChargeState",
"GetCleanSum",
"GetError",
- "GetFanSpeed",
- "SetFanSpeed",
"GetPos",
]
# fmt: off
# ordered by file asc
_COMMANDS: list[type[XmlCommand]] = [
+ GetCleanSpeed,
+ SetCleanSpeed,
GetError,
]
# fmt: on
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index 649d20d01..1a0751bc7 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -17,8 +17,8 @@
from deebot_client.event_bus import EventBus
-class GetFanSpeed(XmlCommandWithMessageHandling):
- """GetFanSpeed command."""
+class GetCleanSpeed(XmlCommandWithMessageHandling):
+ """GetCleanSpeed command."""
name = "GetCleanSpeed"
@@ -35,9 +35,9 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
match speed.lower():
case "standard":
- event = FanSpeedEvent(FanSpeedLevel.NORMAL)
+ event = FanSpeedEvent(FanSpeedLevel.STANDARD)
case "strong":
- event = FanSpeedEvent(FanSpeedLevel.MAX)
+ event = FanSpeedEvent(FanSpeedLevel.STRONG)
if event:
event_bus.notify(event)
@@ -46,15 +46,15 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
return HandlingResult.analyse()
-class SetFanSpeed(XmlSetCommand):
- """Set fan speed command."""
+class SetCleanSpeed(XmlSetCommand):
+ """Set clean speed command."""
name = "SetCleanSpeed"
- get_command = GetFanSpeed
+ get_command = GetCleanSpeed
_mqtt_params = MappingProxyType({"speed": InitParam(FanSpeedLevel)})
def __init__(self, speed: FanSpeedLevel | str) -> None:
- if isinstance(speed, int):
- speed = "strong" if speed in [1, 2] else "normal"
+ if isinstance(speed, FanSpeedLevel):
+ speed = speed.name.lower()
super().__init__({"speed": speed})
diff --git a/deebot_client/events/fan_speed.py b/deebot_client/events/fan_speed.py
index 4a8f599e1..00fcc3bb3 100644
--- a/deebot_client/events/fan_speed.py
+++ b/deebot_client/events/fan_speed.py
@@ -14,8 +14,10 @@ class FanSpeedLevel(IntEnum):
# Values should be sort from low to high on their meanings
QUIET = 1000
+ STANDARD = -1
NORMAL = 0
MAX = 1
+ STRONG = -2
MAX_PLUS = 2
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index 740091fce..140fbdb18 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -5,14 +5,12 @@
import pytest
from deebot_client.command import CommandResult
-from deebot_client.commands.xml import GetFanSpeed
-from deebot_client.commands.xml.fan_speed import SetFanSpeed
+from deebot_client.commands.xml import GetCleanSpeed, SetCleanSpeed
from deebot_client.events import FanSpeedEvent, FanSpeedLevel
from deebot_client.message import HandlingState
from tests.commands import assert_command
from . import get_request_xml
-from ..json import assert_set_command
if TYPE_CHECKING:
from deebot_client.events.base import Event
@@ -21,14 +19,14 @@
@pytest.mark.parametrize(
("speed", "expected_event"),
[
- ("standard", FanSpeedEvent(FanSpeedLevel.NORMAL)),
- ("strong", FanSpeedEvent(FanSpeedLevel.MAX)),
+ ("standard", FanSpeedEvent(FanSpeedLevel.STANDARD)),
+ ("strong", FanSpeedEvent(FanSpeedLevel.STRONG)),
],
ids=["standard", "strong"],
)
async def test_get_fan_speed(speed: str, expected_event: Event) -> None:
json = get_request_xml(f"")
- await assert_command(GetFanSpeed(), json, expected_event)
+ await assert_command(GetCleanSpeed(), json, expected_event)
@pytest.mark.parametrize(
@@ -39,18 +37,20 @@ async def test_get_fan_speed(speed: str, expected_event: Event) -> None:
async def test_get_fan_speed_error(xml: str) -> None:
json = get_request_xml(xml)
await assert_command(
- GetFanSpeed(),
+ GetCleanSpeed(),
json,
None,
command_result=CommandResult(HandlingState.ANALYSE_LOGGED),
)
+
async def test_set_fan_speed() -> None:
- command = SetFanSpeed(FanSpeedLevel.MAX)
+ command = SetCleanSpeed(FanSpeedLevel.STRONG)
json = get_request_xml("")
await assert_command(command, json, None, command_result=CommandResult(HandlingState.SUCCESS))
+
async def test_set_fan_speed_error() -> None:
- command = SetFanSpeed("invalid")
+ command = SetCleanSpeed("invalid")
json = get_request_xml("")
await assert_command(command, json, None, command_result=CommandResult(HandlingState.FAILED))
From 5ae93c39ffb1a3acac2460b168865f229d385d5b Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Tue, 21 Jan 2025 19:39:33 +0100
Subject: [PATCH 03/12] XML SetCleanSpeed: Adjust NAME attribute
---
deebot_client/commands/xml/fan_speed.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index e2d7d17c0..ce4989543 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -49,7 +49,7 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
class SetCleanSpeed(XmlSetCommand):
"""Set clean speed command."""
- name = "SetCleanSpeed"
+ NAME = "SetCleanSpeed"
get_command = GetCleanSpeed
_mqtt_params = MappingProxyType({"speed": InitParam(FanSpeedLevel)})
From f80cc31e93dca2fc4e6c64c4eb66a697b221a8a5 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 21 Jan 2025 19:00:14 +0000
Subject: [PATCH 04/12] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
deebot_client/commands/xml/__init__.py | 5 ++---
deebot_client/commands/xml/fan_speed.py | 1 -
tests/commands/xml/test_fan_speed.py | 8 ++++++--
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/deebot_client/commands/xml/__init__.py b/deebot_client/commands/xml/__init__.py
index d9d70295c..5c6070c3a 100644
--- a/deebot_client/commands/xml/__init__.py
+++ b/deebot_client/commands/xml/__init__.py
@@ -8,7 +8,6 @@
from .charge_state import GetChargeState
from .error import GetError
-
from .fan_speed import GetCleanSpeed, SetCleanSpeed
from .life_span import GetLifeSpan
from .play_sound import PlaySound
@@ -19,14 +18,14 @@
from .common import XmlCommand
__all__ = [
- "GetCleanSpeed",
- "SetCleanSpeed",
"GetChargeState",
+ "GetCleanSpeed",
"GetCleanSum",
"GetError",
"GetLifeSpan",
"GetPos",
"PlaySound",
+ "SetCleanSpeed",
]
# fmt: off
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index ce4989543..fb46a01d9 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -57,4 +57,3 @@ def __init__(self, speed: FanSpeedLevel | str) -> None:
if isinstance(speed, FanSpeedLevel):
speed = speed.name.lower()
super().__init__({"speed": speed})
-
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index 140fbdb18..21eba4a1a 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -47,10 +47,14 @@ async def test_get_fan_speed_error(xml: str) -> None:
async def test_set_fan_speed() -> None:
command = SetCleanSpeed(FanSpeedLevel.STRONG)
json = get_request_xml("")
- await assert_command(command, json, None, command_result=CommandResult(HandlingState.SUCCESS))
+ await assert_command(
+ command, json, None, command_result=CommandResult(HandlingState.SUCCESS)
+ )
async def test_set_fan_speed_error() -> None:
command = SetCleanSpeed("invalid")
json = get_request_xml("")
- await assert_command(command, json, None, command_result=CommandResult(HandlingState.FAILED))
+ await assert_command(
+ command, json, None, command_result=CommandResult(HandlingState.FAILED)
+ )
From 9c69dc73281745f32e12da6a0ab4d80cb031bb04 Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Tue, 21 Jan 2025 20:08:51 +0100
Subject: [PATCH 05/12] Improve tests for XML clean_speed commands
---
tests/commands/xml/test_fan_speed.py | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index 21eba4a1a..7924a2777 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -4,7 +4,7 @@
import pytest
-from deebot_client.command import CommandResult
+from deebot_client.command import CommandResult, CommandWithMessageHandling
from deebot_client.commands.xml import GetCleanSpeed, SetCleanSpeed
from deebot_client.events import FanSpeedEvent, FanSpeedLevel
from deebot_client.message import HandlingState
@@ -24,7 +24,7 @@
],
ids=["standard", "strong"],
)
-async def test_get_fan_speed(speed: str, expected_event: Event) -> None:
+async def test_get_clean_speed(speed: str, expected_event: Event) -> None:
json = get_request_xml(f"")
await assert_command(GetCleanSpeed(), json, expected_event)
@@ -34,7 +34,7 @@ async def test_get_fan_speed(speed: str, expected_event: Event) -> None:
["", ""],
ids=["error", "no_state"],
)
-async def test_get_fan_speed_error(xml: str) -> None:
+async def test_get_clean_speed_error(xml: str) -> None:
json = get_request_xml(xml)
await assert_command(
GetCleanSpeed(),
@@ -44,17 +44,15 @@ async def test_get_fan_speed_error(xml: str) -> None:
)
-async def test_set_fan_speed() -> None:
- command = SetCleanSpeed(FanSpeedLevel.STRONG)
- json = get_request_xml("")
- await assert_command(
- command, json, None, command_result=CommandResult(HandlingState.SUCCESS)
- )
-
-
-async def test_set_fan_speed_error() -> None:
- command = SetCleanSpeed("invalid")
- json = get_request_xml("")
+@pytest.mark.parametrize(
+ ("command", "xml", "result"),
+ [
+ (SetCleanSpeed(FanSpeedLevel.STRONG), "", HandlingState.SUCCESS),
+ (SetCleanSpeed("invalid"), "", HandlingState.FAILED)
+ ]
+)
+async def test_set_clean_speed(command: CommandWithMessageHandling, xml: str, result: HandlingState) -> None:
+ json = get_request_xml(xml)
await assert_command(
- command, json, None, command_result=CommandResult(HandlingState.FAILED)
+ command, json, None, command_result=CommandResult(result)
)
From faa24eaefc47461cc58bc1729689c67faffa2e1f Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 21 Jan 2025 19:15:18 +0000
Subject: [PATCH 06/12] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
tests/commands/xml/test_fan_speed.py | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index 7924a2777..673e5c3fa 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -47,12 +47,16 @@ async def test_get_clean_speed_error(xml: str) -> None:
@pytest.mark.parametrize(
("command", "xml", "result"),
[
- (SetCleanSpeed(FanSpeedLevel.STRONG), "", HandlingState.SUCCESS),
- (SetCleanSpeed("invalid"), "", HandlingState.FAILED)
- ]
+ (
+ SetCleanSpeed(FanSpeedLevel.STRONG),
+ "",
+ HandlingState.SUCCESS,
+ ),
+ (SetCleanSpeed("invalid"), "", HandlingState.FAILED),
+ ],
)
-async def test_set_clean_speed(command: CommandWithMessageHandling, xml: str, result: HandlingState) -> None:
+async def test_set_clean_speed(
+ command: CommandWithMessageHandling, xml: str, result: HandlingState
+) -> None:
json = get_request_xml(xml)
- await assert_command(
- command, json, None, command_result=CommandResult(result)
- )
+ await assert_command(command, json, None, command_result=CommandResult(result))
From ca7ed53eaa113f40217e2b7a309f2219d010ce12 Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Wed, 22 Jan 2025 21:02:40 +0100
Subject: [PATCH 07/12] xml clean_speed: add XmlGetCommand
---
deebot_client/commands/xml/common.py | 29 ++++++++++++++++++++++---
deebot_client/commands/xml/fan_speed.py | 17 ++++++++++++---
2 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/deebot_client/commands/xml/common.py b/deebot_client/commands/xml/common.py
index ac026df71..ba21729fd 100644
--- a/deebot_client/commands/xml/common.py
+++ b/deebot_client/commands/xml/common.py
@@ -3,15 +3,25 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from typing import TYPE_CHECKING, cast
+from typing import TYPE_CHECKING, Any, cast
from xml.etree.ElementTree import Element, SubElement
from defusedxml import ElementTree # type: ignore[import-untyped]
-from deebot_client.command import Command, CommandWithMessageHandling, SetCommand
+from deebot_client.command import (
+ Command,
+ CommandWithMessageHandling,
+ GetCommand,
+ SetCommand,
+)
from deebot_client.const import DataType
from deebot_client.logging_filter import get_logger
-from deebot_client.message import HandlingResult, HandlingState, MessageStr
+from deebot_client.message import (
+ HandlingResult,
+ HandlingState,
+ MessageBodyDataDict,
+ MessageStr,
+)
if TYPE_CHECKING:
from deebot_client.event_bus import EventBus
@@ -86,3 +96,16 @@ class XmlSetCommand(ExecuteCommand, SetCommand, ABC):
Command needs to be linked to the "get" command, for handling (updating) the sensors.
"""
+
+
+class XmlGetCommand(
+ XmlCommandWithMessageHandling, MessageBodyDataDict, GetCommand, ABC
+):
+ """Xml get command."""
+
+ @classmethod
+ def handle_set_args(
+ cls, event_bus: EventBus, args: dict[str, Any]
+ ) -> HandlingResult:
+ """Handle arguments of set command."""
+ return cls._handle_body_data_dict(event_bus, args)
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index fb46a01d9..53ef9b1cb 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -3,13 +3,13 @@
from __future__ import annotations
from types import MappingProxyType
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any
from deebot_client.command import InitParam
from deebot_client.events import FanSpeedEvent, FanSpeedLevel
from deebot_client.message import HandlingResult
-from .common import XmlCommandWithMessageHandling, XmlSetCommand
+from .common import XmlGetCommand, XmlSetCommand
if TYPE_CHECKING:
from xml.etree.ElementTree import Element
@@ -17,11 +17,22 @@
from deebot_client.event_bus import EventBus
-class GetCleanSpeed(XmlCommandWithMessageHandling):
+class GetCleanSpeed(XmlGetCommand):
"""GetCleanSpeed command."""
NAME = "GetCleanSpeed"
+ @classmethod
+ def _handle_body_data_dict(
+ cls, event_bus: EventBus, data: dict[str, Any]
+ ) -> HandlingResult:
+ """Handle message->body->data and notify the correct event subscribers.
+
+ :return: A message response
+ """
+ event_bus.notify(FanSpeedEvent(FanSpeedLevel(int(data["speed"]))))
+ return HandlingResult.success()
+
@classmethod
def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
"""Handle xml message and notify the correct event subscribers.
From dd7a3dd6587df62ef2f65761dbfdff5e86f997ca Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Thu, 23 Jan 2025 18:54:38 +0100
Subject: [PATCH 08/12] Implement handle_set_args of XmlGetCommand in every
command
---
deebot_client/commands/xml/common.py | 7 ++-----
deebot_client/commands/xml/fan_speed.py | 6 +++---
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/deebot_client/commands/xml/common.py b/deebot_client/commands/xml/common.py
index ba21729fd..22b7d3935 100644
--- a/deebot_client/commands/xml/common.py
+++ b/deebot_client/commands/xml/common.py
@@ -19,7 +19,6 @@
from deebot_client.message import (
HandlingResult,
HandlingState,
- MessageBodyDataDict,
MessageStr,
)
@@ -98,14 +97,12 @@ class XmlSetCommand(ExecuteCommand, SetCommand, ABC):
"""
-class XmlGetCommand(
- XmlCommandWithMessageHandling, MessageBodyDataDict, GetCommand, ABC
-):
+class XmlGetCommand(XmlCommandWithMessageHandling, GetCommand, ABC):
"""Xml get command."""
@classmethod
+ @abstractmethod
def handle_set_args(
cls, event_bus: EventBus, args: dict[str, Any]
) -> HandlingResult:
"""Handle arguments of set command."""
- return cls._handle_body_data_dict(event_bus, args)
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index 53ef9b1cb..da954a51c 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -23,14 +23,14 @@ class GetCleanSpeed(XmlGetCommand):
NAME = "GetCleanSpeed"
@classmethod
- def _handle_body_data_dict(
- cls, event_bus: EventBus, data: dict[str, Any]
+ def handle_set_args(
+ cls, event_bus: EventBus, args: dict[str, Any]
) -> HandlingResult:
"""Handle message->body->data and notify the correct event subscribers.
:return: A message response
"""
- event_bus.notify(FanSpeedEvent(FanSpeedLevel(int(data["speed"]))))
+ event_bus.notify(FanSpeedEvent(FanSpeedLevel(int(args["speed"]))))
return HandlingResult.success()
@classmethod
From 4c6e8628cad77067cd84e16895238ca5f867f887 Mon Sep 17 00:00:00 2001
From: Robert Resch
Date: Wed, 29 Jan 2025 12:58:40 +0000
Subject: [PATCH 09/12] enable p2p for xml commands
---
deebot_client/commands/__init__.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/deebot_client/commands/__init__.py b/deebot_client/commands/__init__.py
index 5fb527c47..fc4574353 100644
--- a/deebot_client/commands/__init__.py
+++ b/deebot_client/commands/__init__.py
@@ -11,6 +11,9 @@
COMMANDS as JSON_COMMANDS,
COMMANDS_WITH_MQTT_P2P_HANDLING as JSON_COMMANDS_WITH_MQTT_P2P_HANDLING,
)
+from .xml import (
+ COMMANDS_WITH_MQTT_P2P_HANDLING as XML_COMMANDS_WITH_MQTT_P2P_HANDLING,
+)
if TYPE_CHECKING:
from deebot_client.command import Command, CommandMqttP2P
@@ -18,7 +21,8 @@
COMMANDS: dict[DataType, dict[str, type[Command]]] = {DataType.JSON: JSON_COMMANDS}
COMMANDS_WITH_MQTT_P2P_HANDLING: dict[DataType, dict[str, type[CommandMqttP2P]]] = {
- DataType.JSON: JSON_COMMANDS_WITH_MQTT_P2P_HANDLING
+ DataType.JSON: JSON_COMMANDS_WITH_MQTT_P2P_HANDLING,
+ DataType.XML: XML_COMMANDS_WITH_MQTT_P2P_HANDLING,
}
From 9a239b24aec109113dafdc74c05957a2f028a417 Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Sun, 9 Feb 2025 18:44:53 +0100
Subject: [PATCH 10/12] FanSpeed: extend for XML enum
---
deebot_client/commands/xml/fan_speed.py | 23 +++++--------------
deebot_client/events/fan_speed.py | 30 ++++++++++++++++++++-----
tests/commands/xml/test_fan_speed.py | 11 +++++----
3 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index da954a51c..c275a3180 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -30,7 +30,7 @@ def handle_set_args(
:return: A message response
"""
- event_bus.notify(FanSpeedEvent(FanSpeedLevel(int(args["speed"]))))
+ event_bus.notify(FanSpeedEvent(FanSpeedLevel.from_xml(str(args["speed"]))))
return HandlingResult.success()
@classmethod
@@ -42,19 +42,8 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult:
if xml.attrib.get("ret") != "ok" or not (speed := xml.attrib.get("speed")):
return HandlingResult.analyse()
- event: FanSpeedEvent | None = None
-
- match speed.lower():
- case "standard":
- event = FanSpeedEvent(FanSpeedLevel.STANDARD)
- case "strong":
- event = FanSpeedEvent(FanSpeedLevel.STRONG)
-
- if event:
- event_bus.notify(event)
- return HandlingResult.success()
-
- return HandlingResult.analyse()
+ event_bus.notify(FanSpeedEvent(FanSpeedLevel.from_xml(speed)))
+ return HandlingResult.success()
class SetCleanSpeed(XmlSetCommand):
@@ -64,7 +53,5 @@ class SetCleanSpeed(XmlSetCommand):
get_command = GetCleanSpeed
_mqtt_params = MappingProxyType({"speed": InitParam(FanSpeedLevel)})
- def __init__(self, speed: FanSpeedLevel | str) -> None:
- if isinstance(speed, FanSpeedLevel):
- speed = speed.name.lower()
- super().__init__({"speed": speed})
+ def __init__(self, speed: FanSpeedLevel) -> None:
+ super().__init__({"speed": speed.xml_value})
diff --git a/deebot_client/events/fan_speed.py b/deebot_client/events/fan_speed.py
index 00fcc3bb3..a350f3ba9 100644
--- a/deebot_client/events/fan_speed.py
+++ b/deebot_client/events/fan_speed.py
@@ -4,6 +4,7 @@
from dataclasses import dataclass
from enum import IntEnum, unique
+from typing import Self
from .base import Event
@@ -12,13 +13,30 @@
class FanSpeedLevel(IntEnum):
"""Enum class for all possible fan speed levels."""
+ xml_value: str
+
+ def __new__(cls, value: int, xml_value: str = "") -> Self:
+ """Get new instance."""
+ obj = int.__new__(cls)
+ obj._value_ = value
+ obj.xml_value = xml_value
+ return obj
+
+ @classmethod
+ def from_xml(cls, value: str) -> FanSpeedLevel:
+ """Get FanSpeedLevel from xml value."""
+ for fan_speed_level in FanSpeedLevel:
+ if fan_speed_level.xml_value == value:
+ return fan_speed_level
+
+ msg = f"{value} is not a valid {cls.__name__}"
+ raise ValueError(msg)
+
# Values should be sort from low to high on their meanings
- QUIET = 1000
- STANDARD = -1
- NORMAL = 0
- MAX = 1
- STRONG = -2
- MAX_PLUS = 2
+ QUIET = 1000, ""
+ NORMAL = 0, "standard"
+ MAX = 1, "strong"
+ MAX_PLUS = 2, ""
@dataclass(frozen=True)
diff --git a/tests/commands/xml/test_fan_speed.py b/tests/commands/xml/test_fan_speed.py
index 673e5c3fa..cafebe990 100644
--- a/tests/commands/xml/test_fan_speed.py
+++ b/tests/commands/xml/test_fan_speed.py
@@ -19,8 +19,8 @@
@pytest.mark.parametrize(
("speed", "expected_event"),
[
- ("standard", FanSpeedEvent(FanSpeedLevel.STANDARD)),
- ("strong", FanSpeedEvent(FanSpeedLevel.STRONG)),
+ ("standard", FanSpeedEvent(FanSpeedLevel.NORMAL)),
+ ("strong", FanSpeedEvent(FanSpeedLevel.MAX)),
],
ids=["standard", "strong"],
)
@@ -31,8 +31,8 @@ async def test_get_clean_speed(speed: str, expected_event: Event) -> None:
@pytest.mark.parametrize(
"xml",
- ["", ""],
- ids=["error", "no_state"],
+ [""],
+ ids=["error"],
)
async def test_get_clean_speed_error(xml: str) -> None:
json = get_request_xml(xml)
@@ -48,11 +48,10 @@ async def test_get_clean_speed_error(xml: str) -> None:
("command", "xml", "result"),
[
(
- SetCleanSpeed(FanSpeedLevel.STRONG),
+ SetCleanSpeed(FanSpeedLevel.MAX),
"",
HandlingState.SUCCESS,
),
- (SetCleanSpeed("invalid"), "", HandlingState.FAILED),
],
)
async def test_set_clean_speed(
From 07cd5dbf20f6763288a8d2e5aa88960793efdb7a Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Mon, 10 Feb 2025 23:11:27 +0100
Subject: [PATCH 11/12] XML command SetCleanSpeed: accept speed as string
---
deebot_client/commands/xml/fan_speed.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/deebot_client/commands/xml/fan_speed.py b/deebot_client/commands/xml/fan_speed.py
index c275a3180..7130b0d68 100644
--- a/deebot_client/commands/xml/fan_speed.py
+++ b/deebot_client/commands/xml/fan_speed.py
@@ -53,5 +53,7 @@ class SetCleanSpeed(XmlSetCommand):
get_command = GetCleanSpeed
_mqtt_params = MappingProxyType({"speed": InitParam(FanSpeedLevel)})
- def __init__(self, speed: FanSpeedLevel) -> None:
- super().__init__({"speed": speed.xml_value})
+ def __init__(self, speed: FanSpeedLevel | str) -> None:
+ if isinstance(speed, FanSpeedLevel):
+ speed = speed.xml_value
+ super().__init__({"speed": speed})
From 770d72d2bfea707151339cf098bbf5224f20561d Mon Sep 17 00:00:00 2001
From: flubshi <4031504+flubshi@users.noreply.github.com>
Date: Thu, 13 Feb 2025 22:53:33 +0100
Subject: [PATCH 12/12] WIP: MqttP2P
---
deebot_client/command.py | 8 +++++---
deebot_client/commands/xml/common.py | 23 ++++++++++++++++++++++-
2 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/deebot_client/command.py b/deebot_client/command.py
index dc0e0cf38..6afb94be7 100644
--- a/deebot_client/command.py
+++ b/deebot_client/command.py
@@ -323,9 +323,11 @@ def _pop_or_raise(name: str, type_: type, data: dict[str, Any]) -> Any:
value = data.pop(name)
try:
return type_(value)
- except ValueError as err:
- msg = f'Could not convert "{value}" of {name} into {type_}'
- raise DeebotError(msg) from err
+ except ValueError:
+ # TODO: Workaround to map out custom enums
+ return type_.from_xml(value)
+ # msg = f'Could not convert "{value}" of {name} into {type_}'
+ # raise DeebotError(msg) from err
class GetCommand(CommandWithMessageHandling, ABC):
diff --git a/deebot_client/commands/xml/common.py b/deebot_client/commands/xml/common.py
index 22b7d3935..77a78b8e5 100644
--- a/deebot_client/commands/xml/common.py
+++ b/deebot_client/commands/xml/common.py
@@ -10,6 +10,7 @@
from deebot_client.command import (
Command,
+ CommandMqttP2P,
CommandWithMessageHandling,
GetCommand,
SetCommand,
@@ -90,7 +91,27 @@ def _handle_xml(cls, _: EventBus, xml: Element) -> HandlingResult:
return HandlingResult(HandlingState.FAILED)
-class XmlSetCommand(ExecuteCommand, SetCommand, ABC):
+class XmlCommandMqttP2P(XmlCommand, CommandMqttP2P, ABC):
+ """Json base command for mqtt p2p channel."""
+
+ @classmethod
+ def create_from_mqtt(cls, payload: str | bytes | bytearray) -> CommandMqttP2P:
+ """Create a command from the mqtt data."""
+ xml = ElementTree.fromstring(payload)
+ return cls._create_from_mqtt(xml.attrib)
+
+ def handle_mqtt_p2p(
+ self, event_bus: EventBus, response_payload: str | bytes | bytearray
+ ) -> None:
+ """Handle response received over the mqtt channel "p2p"."""
+ self._handle_mqtt_p2p(event_bus, response_payload)
+
+ @abstractmethod
+ def _handle_mqtt_p2p(self, event_bus: EventBus, response: dict[str, Any]) -> None:
+ """Handle response received over the mqtt channel "p2p"."""
+
+
+class XmlSetCommand(ExecuteCommand, SetCommand, XmlCommandMqttP2P, ABC):
"""Xml base set command.
Command needs to be linked to the "get" command, for handling (updating) the sensors.