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 support for MQTT + XML based devices #288

Draft
wants to merge 128 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 111 commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
ec10b21
Added uses_xml_protocal property
sandervankasteel Aug 6, 2023
c04d466
Added groundwork for handling XML based messages
sandervankasteel Aug 6, 2023
f6b94f5
Split up command sending in both XML and JSON paths
sandervankasteel Aug 6, 2023
4dd2dfb
Added initial support for handling battery messages
sandervankasteel Aug 6, 2023
e336209
Added xml_name property for XML based devices
sandervankasteel Aug 6, 2023
c829057
Added Charge command for XML based devices
sandervankasteel Aug 6, 2023
b4f69fe
WIP
sandervankasteel Aug 6, 2023
6a02f72
Added initial support for cleaning for XML based devices
sandervankasteel Aug 6, 2023
7d83fd7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2023
f111426
Added broken teset for getBattery over MQTT and XML
sandervankasteel Aug 7, 2023
e8a390a
Added error code 8
sandervankasteel Aug 7, 2023
b84a6b3
Added additional DustCaseHeap for the Deebot 900
sandervankasteel Aug 7, 2023
22be790
Added xml_name for the clean_logs
sandervankasteel Aug 7, 2023
303c907
Added comment on a potential way to tackle the one difference between…
sandervankasteel Aug 7, 2023
cdede25
Added a bit more debugging
sandervankasteel Aug 7, 2023
43b0930
Only set cleaning speed if not supplied and if we are using the MQTT …
sandervankasteel Aug 7, 2023
3abd6c1
Raise NotImplementedError on ChargeState XML body handling
sandervankasteel Aug 7, 2023
f45a48d
WIP
sandervankasteel Aug 7, 2023
43d0a97
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2023
0ec8fb6
Added rough ChargeState implementation, always returns 'DOCKED', for now
sandervankasteel Aug 7, 2023
4b91af9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2023
249310f
Satisfy linter a bit more
sandervankasteel Aug 7, 2023
77fc259
Added more comments
sandervankasteel Aug 7, 2023
082583b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2023
e0530e4
Made parameters inline with the rest of the codebase
sandervankasteel Aug 8, 2023
8a96b69
Added FanSpeedXml Enum for the MQTT + XML based devices
sandervankasteel Aug 8, 2023
eed2d28
Implemented basic Getting Clean / Fan Speed
sandervankasteel Aug 8, 2023
b6c12fd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 8, 2023
28add27
Satify pre-commit.ci
sandervankasteel Aug 8, 2023
7a2cf8d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 8, 2023
a6f922a
Updated docs
sandervankasteel Aug 9, 2023
ad4d016
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Aug 9, 2023
c175c94
Clean up
sandervankasteel Aug 9, 2023
068dfd8
Allow setting fanspeed with FanSpeedLevelXml-enum
sandervankasteel Aug 9, 2023
48148ac
Handle success messages from the XML based API
sandervankasteel Aug 9, 2023
887232b
Updated charge_state
sandervankasteel Aug 9, 2023
a2fdb38
Added tests for fan_seep
sandervankasteel Aug 9, 2023
f7c0b37
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2023
093ce7b
Fix getting correct attribute
sandervankasteel Aug 9, 2023
fe75da1
Sastify mypy
sandervankasteel Aug 9, 2023
cf94b30
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2023
f108a28
Added xml_name for GetVolume
sandervankasteel Aug 9, 2023
1583b81
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Aug 9, 2023
76b5988
Added code comment
sandervankasteel Aug 9, 2023
8f8f197
Aligned arguments
sandervankasteel Aug 9, 2023
983e2ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 9, 2023
cead5a2
Fixed potential KeyError
sandervankasteel Aug 9, 2023
7b3501b
Use 'is' instead of ==
sandervankasteel Aug 9, 2023
6bb708e
Added a bunch of xml_name properties and added some handle_body_data_…
sandervankasteel Aug 10, 2023
b6f7a95
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 10, 2023
dd8a617
Implemented basic test for getting errors
sandervankasteel Aug 10, 2023
9304bfd
Implemented xml_ handler for Errors
sandervankasteel Aug 10, 2023
d183b8a
Added get_request_xml helper for testing
sandervankasteel Aug 10, 2023
bb4d236
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 10, 2023
51c05c7
Added more TODOs
sandervankasteel Aug 10, 2023
57d1789
Implemented charge command response handling
sandervankasteel Aug 11, 2023
ca647dc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2023
e8508ca
Implemented GetCleanInfo for XML based models
sandervankasteel Aug 11, 2023
6a6189b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2023
b1b9f48
Sastify mypy
sandervankasteel Aug 11, 2023
580c22d
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Aug 11, 2023
696fd71
Cleaned up charge_state and fixed small issue with not being able to …
sandervankasteel Aug 11, 2023
3c24ecd
Swapped around VacuumState IDLE and returning
sandervankasteel Aug 11, 2023
2216290
Remove unused import
sandervankasteel Aug 11, 2023
a420445
Added _handle_body_data_xml to Volume
sandervankasteel Aug 11, 2023
45ab8d1
Set XML name for GetPos command
sandervankasteel Aug 11, 2023
6996cbe
Added tests for charge_state for XML devices
sandervankasteel Aug 11, 2023
6fa3620
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 11, 2023
028755c
Implemented separate commands for getting the LifeSpan for XML based …
sandervankasteel Aug 12, 2023
fb7ab6f
Added xml_name for the currently unimplemented commands
sandervankasteel Aug 12, 2023
13cd608
Clean up
sandervankasteel Aug 12, 2023
e215319
Added unimplemented version of the GetReportStats for the XML api
sandervankasteel Aug 12, 2023
b0929c2
Fixed issue that in some cases XML messages where parsed to the JSON …
sandervankasteel Aug 12, 2023
ba7b68b
Fixed typo in payloadType
sandervankasteel Aug 12, 2023
f0d55ca
fixed typo
sandervankasteel Aug 12, 2023
a281e37
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 12, 2023
b098c2c
Satisfied mypy a bit more
sandervankasteel Aug 13, 2023
040a39c
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Aug 13, 2023
e61eaf7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2023
1b7778e
Added docs
sandervankasteel Aug 13, 2023
9794514
Fixed missing import and added a comment
sandervankasteel Aug 13, 2023
8619ef7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2023
440cbd9
Satified mypy a bit more
sandervankasteel Aug 13, 2023
00ef119
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Aug 13, 2023
020a62e
Merge branch 'dev' into feature/AddXmlDatatypeSupport
sandervankasteel Aug 29, 2023
756ee05
Did some initial work for moving the cleaning XML command
sandervankasteel Oct 10, 2023
2ddf6fd
Merge branch 'dev' into pr/sandervankasteel/288
edenhaus Oct 21, 2023
0e54f35
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 21, 2023
559ca80
remove todo
edenhaus Oct 21, 2023
41652c7
remove unrequired code
edenhaus Oct 21, 2023
2e20fe6
move xml tests into it's own folder
edenhaus Oct 21, 2023
87ef3e2
move xml messages into it'sown folder
edenhaus Oct 21, 2023
1ef36cd
Move some xml commands into it's own folder
edenhaus Oct 21, 2023
30469e2
Move xml commands to it's own folder
edenhaus Oct 21, 2023
05e2323
remove duplicate
edenhaus Oct 21, 2023
0db54e9
WIP
sandervankasteel Oct 24, 2023
4213e03
WIP
sandervankasteel Oct 24, 2023
0e65899
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2023
c0b6ff6
WIP
sandervankasteel Nov 12, 2023
2bd2185
Merge branch 'dev' into feature/AddXmlDatatypeSupport
sandervankasteel Nov 12, 2023
9e07260
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2023
ec2428b
Updated references from VacuumState to State
sandervankasteel Nov 12, 2023
7414227
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Nov 12, 2023
120b9e6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2023
114ae95
Renamed ls1ok3
sandervankasteel Nov 12, 2023
8567796
Merge branch 'feature/AddXmlDatatypeSupport' of github.com:sandervank…
sandervankasteel Nov 12, 2023
8bb8d48
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2023
4df1fa4
Removed debugging statements
sandervankasteel Nov 12, 2023
b4992a8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2023
9c8394d
fix some ruff findings
edenhaus Nov 15, 2023
f48c9bc
create MessageStr
edenhaus Nov 15, 2023
b26d8a1
Add and use MessageBodyXml
edenhaus Nov 15, 2023
d6cd140
add missing capability
edenhaus Nov 15, 2023
231a85b
Merge branch 'dev' into feature/AddXmlDatatypeSupport
edenhaus Nov 15, 2023
3eaf453
use XmlCommandWithMessageHandling
edenhaus Nov 15, 2023
9734d16
Add test for xml based capabilities
edenhaus Nov 15, 2023
fc61b30
fix mypy findings
edenhaus Nov 15, 2023
381f054
fix GetError
edenhaus Nov 15, 2023
33d5fbf
Re-implemented GetChargeState
sandervankasteel Nov 15, 2023
8b82111
Removed GetCleanInfo for the ls1ok3
sandervankasteel Nov 15, 2023
22867ab
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2023
84dfede
Implemented GetFanSpeed
sandervankasteel Nov 15, 2023
fd05293
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 15, 2023
bc6ec8c
remove duplicated code
edenhaus Nov 16, 2023
7ee70a3
Merge branch 'dev' into pr/sandervankasteel/288
edenhaus Dec 12, 2023
a332d74
Remove comment out code
edenhaus Dec 12, 2023
3f459c3
fix ruff finding
edenhaus Dec 12, 2023
d72d13d
remove some classes, which looks the same as the json one
edenhaus Dec 12, 2023
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
2 changes: 1 addition & 1 deletion deebot_client/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ async def execute(

Returns
-------
bot_reached (bool): True if the command was targeting the bot and it responded in time. False otherwise.
bot_reached (bool): True if the command was targeting the bot, and it responded in time. False otherwise.
This value is not indicating if the command was executed successfully.
"""
try:
Expand Down
2 changes: 1 addition & 1 deletion deebot_client/commands/json/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def _handle_body_data_dict(
return HandlingResult.analyse()


# from https://github.com/mrbungle64/ecovacs-deebot.js/blob/master/library/errorCodes.js
# from https://github.com/mrbungle64/ecovacs-deebot.js/blob/master/library/errorCodes.json
_ERROR_CODES = {
-3: "Error parsing response data",
-2: "Internal error",
Expand Down
158 changes: 158 additions & 0 deletions deebot_client/commands/xml/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""Commands module."""
from deebot_client.command import Command, CommandMqttP2P

# from .advanced_mode import GetAdvancedMode, SetAdvancedMode
from .battery import GetBattery

# from .carpet import GetCarpetAutoFanBoost, SetCarpetAutoFanBoost
from .charge import Charge

# from .charge_state import GetChargeState
# from .clean import Clean, CleanArea, GetCleanInfo
# from .clean_count import GetCleanCount, SetCleanCount
from .clean_logs import GetCleanLogs

# from .clean_preference import GetCleanPreference, SetCleanPreference
from .common import XmlCommand

# from .continuous_cleaning import GetContinuousCleaning, SetContinuousCleaning
from .error import GetError

# from .fan_speed import GetFanSpeed, SetFanSpeed
# from .life_span import GetLifeSpan, ResetLifeSpan
from .map import (
GetCachedMapInfo,
GetMajorMap,
GetMapSet,
GetMapSubSet,
GetMapTrace,
GetMinorMap,
)

# from .multimap_state import GetMultimapState, SetMultimapState
from .play_sound import PlaySound

# from .pos import GetPos
from .relocation import SetRelocationState
from .stats import GetStats, GetTotalStats

# from .true_detect import GetTrueDetect, SetTrueDetect
# from .volume import GetVolume, SetVolume
# from .water_info import GetWaterInfo, SetWaterInfo

__all__ = [
# "GetAdvancedMode",
# "SetAdvancedMode",
"GetBattery",
# "GetCarpetAutoFanBoost",
# "SetCarpetAutoFanBoost",
# "GetCleanCount",
# "SetCleanCount",
# "GetCleanPreference",
# "SetCleanPreference",
"Charge",
# "GetChargeState",
# "Clean",
# "CleanArea",
# "GetCleanInfo",
"GetCleanLogs",
# "GetContinuousCleaning",
# "SetContinuousCleaning",
"GetError",
"GetCachedMapInfo",
"GetMajorMap",
"GetMapSet",
"GetMapSubSet",
"GetMapTrace",
"GetMinorMap",
# "GetMultimapState",
# "SetMultimapState",
"PlaySound",
"SetRelocationState",
"GetStats",
"GetTotalStats",
# "GetTrueDetect",
# "SetTrueDetect",
# "GetVolume",
# "SetVolume",
# "GetWaterInfo",
# "SetWaterInfo",
]

# fmt: off
# ordered by file asc
_COMMANDS: list[type[XmlCommand]] = [
# GetAdvancedMode,
# SetAdvancedMode,

GetBattery,

# GetCarpetAutoFanBoost,
# SetCarpetAutoFanBoost,

# GetCleanCount,
# SetCleanCount,

# GetCleanPreference,
# SetCleanPreference,

Charge,

# GetChargeState,

# Clean,
# CleanArea,
# GetCleanInfo,

GetCleanLogs,

# GetContinuousCleaning,
# SetContinuousCleaning,

GetError,

# GetFanSpeed,
# SetFanSpeed,
#
# GetLifeSpan,
# ResetLifeSpan,

GetCachedMapInfo,
GetMajorMap,
GetMapSet,
GetMapSubSet,
GetMapTrace,
GetMinorMap,

# GetMultimapState,
# SetMultimapState,

PlaySound,

# GetPos,

SetRelocationState,

GetStats,
GetTotalStats,

# GetTrueDetect,
# SetTrueDetect,

# GetVolume,
# SetVolume,

# GetWaterInfo,
# SetWaterInfo,
]
# fmt: on

COMMANDS: dict[str, type[Command]] = {
cmd.name: cmd for cmd in _COMMANDS # type: ignore[misc]
}

COMMANDS_WITH_MQTT_P2P_HANDLING: dict[str, type[CommandMqttP2P]] = {
cmd_name: cmd
for (cmd_name, cmd) in COMMANDS.items()
if issubclass(cmd, CommandMqttP2P)
}
14 changes: 14 additions & 0 deletions deebot_client/commands/xml/battery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Battery commands."""
from deebot_client.messages.xml import OnBattery

from .common import CommandWithMessageHandling


class GetBattery(OnBattery, CommandWithMessageHandling):
"""Get battery command."""

name = "GetBatteryInfo"

def __init__(self, is_available_check: bool = False) -> None:
super().__init__()
self._is_available_check = is_available_check
63 changes: 63 additions & 0 deletions deebot_client/commands/xml/charge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Charge commands."""
from typing import Any

from defusedxml import ElementTree

from deebot_client.authentication import Authenticator
from deebot_client.command import CommandResult
from deebot_client.event_bus import EventBus
from deebot_client.events import StateEvent
from deebot_client.logging_filter import get_logger
from deebot_client.message import HandlingResult
from deebot_client.models import DeviceInfo, State

from .common import ExecuteCommand

_LOGGER = get_logger(__name__)


class Charge(ExecuteCommand):
"""Charge command."""

name = "Charge"

xml_has_own_element = True

@classmethod
def _handle_body(
cls, event_bus: EventBus, body: dict[str, Any] | str
) -> HandlingResult:
"""Handle message->body and notify the correct event subscribers.

:return: A message response
"""

tree = ElementTree.fromstring(body)
attributes = tree.attrib.keys()

if len(attributes) == 0:
return HandlingResult.analyse()

# "resp": "<ctl ret='ok'/>", == returning
if "ret" in attributes and tree.attrib.get("ret") == "ok":
event_bus.notify(StateEvent(State.RETURNING))
return HandlingResult.success()

# "<ctl ret='fail' errno='8'/>", == already charging
is_already_charging = (
"errno" in attributes and int(tree.attrib.get("errno")) == 8
)
if is_already_charging:
# bot is already charging
event_bus.notify(StateEvent(State.DOCKED))
return HandlingResult.success()

return HandlingResult.success()

async def _execute(
self, authenticator: Authenticator, device_info: DeviceInfo, event_bus: EventBus
) -> CommandResult:
if isinstance(self._args, dict):
self._args.update({"type": "go"})

return await super()._execute(authenticator, device_info, event_bus)
102 changes: 102 additions & 0 deletions deebot_client/commands/xml/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# from xml.etree import ElementTree
#
# from deebot_client.authentication import Authenticator
# from deebot_client.command import CommandResult
# from deebot_client.commands.xml.common import ExecuteCommand
# from deebot_client.event_bus import EventBus
# from deebot_client.events import StateEvent
# from deebot_client.message import HandlingResult, MessageBodyDataDict
# from deebot_client.models import CleanAction, CleanMode, DeviceInfo, VacuumState
#
#
# @unique
# class CleanSpeed(str, Enum):
# """Enum class for all possible clean speeds.
#
# Currently only used for the Deebot 900.
# """
#
# STANDARD = "standard"
# STRONG = "strong"
#
#
# class GetCleanInfo(NoArgsCommand, MessageBodyDataDict):
# """Get clean info command."""
#
# name = "GetCleanState"
#
# @classmethod
# def _handle_body_data_xml(
# cls, event_bus: EventBus, xml_message: str
# ) -> HandlingResult:
# status: VacuumState | None = None
#
# tree = ElementTree.fromstring(xml_message)
#
# element = tree.find("clean")
# if element is None:
# return HandlingResult.analyse()
#
# raw_state = element.attrib.get("st")
# a = element.attrib.get("a") # Action ?
#
# if raw_state == "h" and a == "0":
# status = VacuumState.IDLE
# elif raw_state == "p" and a == "0":
# status = VacuumState.PAUSED
# elif raw_state == "s" and a == "0":
# status = VacuumState.CLEANING
# elif raw_state == "h" and a == "1":
# status = VacuumState.RETURNING
#
# if status:
# event_bus.notify(StateEvent(status))
# return HandlingResult.success()
#
# return HandlingResult.analyse()
#
#
# class CleanArea(Clean):
# """Clean area command."""
#
# def __init__(self, mode: CleanMode, area: str, cleanings: int = 1) -> None:
# super().__init__(CleanAction.START)
# if not isinstance(self._args, dict):
# raise ValueError("args must be a dict!")
#
# self._args["type"] = mode.value
# self._args["content"] = str(area)
# self._args["count"] = cleanings
#
#
# class Clean(ExecuteCommand):
# name = "Clean"
#
# has_sub_element = True
#
# async def _execute(
# self, authenticator: Authenticator, device_info: DeviceInfo, event_bus: EventBus
# ) -> CommandResult:
# state = event_bus.get_last_event(StateEvent)
#
# if state and isinstance(self._args, dict):
# if (
# self._args["act"] == CleanAction.RESUME.value
# and state.state != VacuumState.PAUSED
# ):
# self._args = str(self.__get_args(CleanAction.START))[0]
# elif (
# self._args["act"] == CleanAction.START.value
# and state.state == VacuumState.PAUSED
# ):
# self._args = str(self.__get_args(CleanAction.RESUME))[0]
# elif self._args["act"] == CleanAction.START.value:
# self._args["act"] = str(CleanAction.START.value)[0]
#
# elif self._args["act"] == CleanAction.STOP.value:
# self._args["act"] = str(CleanAction.HALT.value)[0]
#
# if "speed" not in self._args:
# self._args["speed"] = CleanSpeed.STANDARD.value
#
# return await super()._execute(authenticator, device_info, event_bus)
Loading
Loading