diff --git a/deebot_client/commands/xml/__init__.py b/deebot_client/commands/xml/__init__.py index ef7e51fab..9561f6581 100644 --- a/deebot_client/commands/xml/__init__.py +++ b/deebot_client/commands/xml/__init__.py @@ -14,7 +14,7 @@ from .fan_speed import GetFanSpeed from .life_span import GetLifeSpan from .play_sound import PlaySound -from .pos import GetPos +from .pos import GetChargerPos, GetPos from .stats import GetCleanSum if TYPE_CHECKING: @@ -24,6 +24,7 @@ "Charge", "GetBatteryInfo", "GetChargeState", + "GetChargerPos", "GetCleanLogs", "GetCleanSum", "GetError", @@ -37,9 +38,11 @@ # ordered by file asc _COMMANDS: list[type[XmlCommand]] = [ GetBatteryInfo, + GetChargerPos, GetCleanLogs, GetError, GetLifeSpan, + GetPos, PlaySound, ] # fmt: on diff --git a/deebot_client/commands/xml/pos.py b/deebot_client/commands/xml/pos.py index c9fed0920..b7c8f16fe 100644 --- a/deebot_client/commands/xml/pos.py +++ b/deebot_client/commands/xml/pos.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING -from deebot_client.events import Position, PositionsEvent from deebot_client.message import HandlingResult +from deebot_client.messages.xml import Pos from deebot_client.rs.map import PositionType from .common import XmlCommandWithMessageHandling @@ -16,7 +16,7 @@ from deebot_client.event_bus import EventBus -class GetPos(XmlCommandWithMessageHandling): +class GetPos(XmlCommandWithMessageHandling, Pos): """GetPos command.""" NAME = "GetPos" @@ -30,13 +30,21 @@ def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult: if xml.attrib.get("ret") != "ok" or xml.attrib.get("t") != "p": return HandlingResult.analyse() - if p := xml.attrib.get("p"): - p_x, p_y = p.split(",", 2) - p_a = xml.attrib.get("a", 0) - position = Position( - type=PositionType.DEEBOT, x=int(p_x), y=int(p_y), a=int(p_a) - ) - event_bus.notify(PositionsEvent(positions=[position])) - return HandlingResult.success() + return cls._parse_xml(PositionType.DEEBOT, event_bus, xml) - return HandlingResult.analyse() + +class GetChargerPos(XmlCommandWithMessageHandling, Pos): + """GetChargerPos command.""" + + NAME = "GetChargerPos" + + @classmethod + def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult: + """Handle xml message and notify the correct event subscribers. + + :return: A message response + """ + if xml.attrib.get("ret") != "ok": + return HandlingResult.analyse() + + return cls._parse_xml(PositionType.CHARGER, event_bus, xml) diff --git a/deebot_client/messages/xml/__init__.py b/deebot_client/messages/xml/__init__.py index 21d5370a9..c0cb694d6 100644 --- a/deebot_client/messages/xml/__init__.py +++ b/deebot_client/messages/xml/__init__.py @@ -5,17 +5,19 @@ from typing import TYPE_CHECKING from deebot_client.messages.xml.battery import BatteryInfo +from deebot_client.messages.xml.pos import Pos if TYPE_CHECKING: from collections.abc import Sequence from deebot_client.message import Message -__all__: Sequence[str] = ["BatteryInfo"] +__all__: Sequence[str] = ["BatteryInfo", "Pos"] # fmt: off # ordered by file asc _MESSAGES: list[type[Message]] = [ - BatteryInfo + BatteryInfo, + Pos ] # fmt: on diff --git a/deebot_client/messages/xml/pos.py b/deebot_client/messages/xml/pos.py new file mode 100644 index 000000000..df6e6c21a --- /dev/null +++ b/deebot_client/messages/xml/pos.py @@ -0,0 +1,42 @@ +"""Pos messages.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from deebot_client.events import Position, PositionsEvent +from deebot_client.message import HandlingResult +from deebot_client.messages.xml.common import XmlMessage +from deebot_client.rs.map import PositionType + +if TYPE_CHECKING: + from xml.etree.ElementTree import Element + + from deebot_client.event_bus import EventBus + + +class Pos(XmlMessage): + """Pos message.""" + + NAME = "Pos" + + @classmethod + def _handle_xml(cls, event_bus: EventBus, xml: Element) -> HandlingResult: + if xml.attrib.get("t") != "p": + return HandlingResult.analyse() + + return cls._parse_xml(PositionType.DEEBOT, event_bus, xml) + + @classmethod + def _parse_xml( + cls, position_type: PositionType, event_bus: EventBus, xml: Element + ) -> HandlingResult: + """Handle xml message and notify the correct event subscribers.""" + if (p := xml.attrib.get("p")) and (xml.attrib.get("valid", "1")) == "1": + p_x, p_y = p.split(",", 2) + p_a = xml.attrib.get("a", 0) + position = Position(type=position_type, x=int(p_x), y=int(p_y), a=int(p_a)) + event_bus.notify(PositionsEvent(positions=[position])) + return HandlingResult.success() + + return HandlingResult.analyse() diff --git a/tests/commands/xml/test_pos.py b/tests/commands/xml/test_pos.py index 6157f1d93..86d5da587 100644 --- a/tests/commands/xml/test_pos.py +++ b/tests/commands/xml/test_pos.py @@ -4,6 +4,7 @@ from deebot_client.command import CommandResult from deebot_client.commands.xml import GetPos +from deebot_client.commands.xml.pos import GetChargerPos from deebot_client.events import Position, PositionsEvent from deebot_client.message import HandlingState from deebot_client.rs.map import PositionType @@ -22,8 +23,13 @@ async def test_get_pos() -> None: @pytest.mark.parametrize( "xml", - ["", ""], - ids=["error", "no_state"], + [ + "", + "", + "", + "", + ], + ids=["error", "no_state", "wrong_type", "not_valid"], ) async def test_get_pos_error(xml: str) -> None: json = get_request_xml(xml) @@ -33,3 +39,26 @@ async def test_get_pos_error(xml: str) -> None: None, command_result=CommandResult(HandlingState.ANALYSE_LOGGED), ) + + +async def test_get_charger_pos() -> None: + json = get_request_xml("") + expected_event = PositionsEvent( + positions=[Position(type=PositionType.CHARGER, x=77, y=-5, a=-3)] + ) + await assert_command(GetChargerPos(), json, expected_event) + + +@pytest.mark.parametrize( + "xml", + ["", ""], + ids=["error", "no_state"], +) +async def test_get_charger_pos_error(xml: str) -> None: + json = get_request_xml(xml) + await assert_command( + GetChargerPos(), + json, + None, + command_result=CommandResult(HandlingState.ANALYSE_LOGGED), + ) diff --git a/tests/messages/xml/test_pos.py b/tests/messages/xml/test_pos.py new file mode 100644 index 000000000..4505e7af7 --- /dev/null +++ b/tests/messages/xml/test_pos.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import pytest + +from deebot_client.events import Position, PositionsEvent +from deebot_client.message import HandlingState +from deebot_client.messages.xml import Pos +from deebot_client.rs.map import PositionType +from tests.messages import assert_message, assert_message_failure + + +@pytest.mark.parametrize("position", [(-9, 15, 89)]) +def test_Pos(position: tuple[int, int, int]) -> None: + x, y, a = position + xml_message = f'' + assert_message( + Pos, + xml_message, + PositionsEvent([Position(type=PositionType.DEEBOT, x=x, y=y, a=a)]), + ) + + +@pytest.mark.parametrize( + "xml_message", + { + '', + '', + '', + }, +) +def test_Pos_error(xml_message: str) -> None: + assert_message_failure(Pos, xml_message, HandlingState.ANALYSE_LOGGED)