From 1ad4ad5125e65fd9ff7961505eeb3acf83c2128e Mon Sep 17 00:00:00 2001 From: maniac103 Date: Tue, 12 Mar 2024 14:52:59 +0100 Subject: [PATCH] [ecovacs] Add support for DEEBOT X2 Omni (#16487) Fixes #16117 * [ecovacs] Interpret empty error code list as 'no error' Newer devices don't explicitly report 'no error' anymore, but instead send an empty list. Signed-off-by: Danny Baumann Signed-off-by: Ciprian Pascu --- .../api/commands/GetErrorCommand.java | 6 ++-- .../api/commands/SpotAreaCleaningCommand.java | 10 ++++-- .../internal/api/impl/JsonReportParser.java | 8 +++-- .../ecovacs/internal/api/model/CleanMode.java | 2 +- .../internal/api/model/DeviceCapability.java | 2 ++ .../handler/EcovacsVacuumHandler.java | 3 +- .../devices/supported_device_list.json | 36 +++++++++++++++++++ 7 files changed, 57 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/GetErrorCommand.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/GetErrorCommand.java index 4b5dcc6788d..4c8b5764b08 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/GetErrorCommand.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/GetErrorCommand.java @@ -44,10 +44,8 @@ public class GetErrorCommand extends IotDeviceCommand> { Gson gson) throws DataParsingException { if (response instanceof PortalIotCommandJsonResponse jsonResponse) { ErrorReport resp = jsonResponse.getResponsePayloadAs(gson, ErrorReport.class); - if (resp.errorCodes.isEmpty()) { - return Optional.empty(); - } - return Optional.of(resp.errorCodes.get(0)); + int responseCode = resp.errorCodes.isEmpty() ? 0 : resp.errorCodes.get(0); + return Optional.of(responseCode); } else { String payload = ((PortalIotCommandXmlResponse) response).getResponsePayloadXml(); return DeviceInfo.parseErrorInfo(payload); diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/SpotAreaCleaningCommand.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/SpotAreaCleaningCommand.java index 92b904d8112..06dd9abcc2e 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/SpotAreaCleaningCommand.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/commands/SpotAreaCleaningCommand.java @@ -21,7 +21,13 @@ import org.eclipse.jdt.annotation.NonNullByDefault; */ @NonNullByDefault public class SpotAreaCleaningCommand extends AbstractAreaCleaningCommand { - public SpotAreaCleaningCommand(List roomIds, int cleanPasses) { - super("spotArea", String.join(",", roomIds), cleanPasses); + public SpotAreaCleaningCommand(List roomIds, int cleanPasses, boolean usesFreeClean) { + super(usesFreeClean ? "freeClean" : "spotArea", prepareRoomIds(roomIds, usesFreeClean), cleanPasses); + } + + private static String prepareRoomIds(List roomIds, boolean usesFreeClean) { + String prefix = usesFreeClean ? "1," : ""; + String separator = usesFreeClean ? ";" : ","; + return prefix + String.join(separator, roomIds); } } diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/impl/JsonReportParser.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/impl/JsonReportParser.java index ae9ed9918bd..0acb6f52b34 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/impl/JsonReportParser.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/impl/JsonReportParser.java @@ -109,8 +109,12 @@ class JsonReportParser implements ReportParser { } case "error": { ErrorReport report = payloadAs(response, ErrorReport.class); - for (Integer code : report.errorCodes) { - listener.onErrorReported(device, code); + if (report.errorCodes.isEmpty()) { + listener.onErrorReported(device, 0); + } else { + for (Integer code : report.errorCodes) { + listener.onErrorReported(device, code); + } } } case "stats": { diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/CleanMode.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/CleanMode.java index 65cd12a6812..ab813befe15 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/CleanMode.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/CleanMode.java @@ -27,7 +27,7 @@ public enum CleanMode { EDGE, @SerializedName("spot") SPOT, - @SerializedName(value = "SpotArea", alternate = { "spotArea" }) + @SerializedName(value = "SpotArea", alternate = { "spotArea", "freeClean" }) SPOT_AREA, @SerializedName(value = "CustomArea", alternate = { "customArea" }) CUSTOM_AREA, diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/DeviceCapability.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/DeviceCapability.java index 286e38644ca..c9cea1422aa 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/DeviceCapability.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/api/model/DeviceCapability.java @@ -29,6 +29,8 @@ public enum DeviceCapability { VOICE_REPORTING, @SerializedName("spot_area_cleaning") SPOT_AREA_CLEANING, + @SerializedName("free_clean_command") + FREE_CLEAN_FOR_SPOT_AREA, @SerializedName("custom_area_cleaning") CUSTOM_AREA_CLEANING, @SerializedName("single_room_cleaning") diff --git a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/handler/EcovacsVacuumHandler.java b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/handler/EcovacsVacuumHandler.java index 34b7ce703df..32a5881ae2c 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/handler/EcovacsVacuumHandler.java +++ b/bundles/org.openhab.binding.ecovacs/src/main/java/org/openhab/binding/ecovacs/internal/handler/EcovacsVacuumHandler.java @@ -761,7 +761,8 @@ public class EcovacsVacuumHandler extends BaseThingHandler implements EcovacsDev } } if (!roomIds.isEmpty()) { - return new SpotAreaCleaningCommand(roomIds, passes); + return new SpotAreaCleaningCommand(roomIds, passes, + device.hasCapability(DeviceCapability.FREE_CLEAN_FOR_SPOT_AREA)); } } else { logger.info("{}: spotArea command needs to have the form spotArea:[;][;<...roomX>][:x2]", diff --git a/bundles/org.openhab.binding.ecovacs/src/main/resources/devices/supported_device_list.json b/bundles/org.openhab.binding.ecovacs/src/main/resources/devices/supported_device_list.json index f68b93f480a..c585cf4baf1 100644 --- a/bundles/org.openhab.binding.ecovacs/src/main/resources/devices/supported_device_list.json +++ b/bundles/org.openhab.binding.ecovacs/src/main/resources/devices/supported_device_list.json @@ -557,5 +557,41 @@ "modelName": "DEEBOT U2 SE", "deviceClass": "zjna8m", "deviceClassLink": "ipzjy0" + }, + + { + "modelName": "DEEBOT X2", + "deviceClass": "e6ofmn", + "protoVersion": "json_v2", + "usesMqtt": true, + "capabilities": [ + "mopping_system", + "main_brush", + "spot_area_cleaning", + "free_clean_command", + "custom_area_cleaning", + "clean_speed_control", + "voice_reporting", + "read_network_info", + "unit_care_lifespan", + "true_detect_3d", + "mapping", + "auto_empty_station" + ] + }, + { + "modelName": "DEEBOT X2 OMNI", + "deviceClass": "lf3bn4", + "deviceClassLink": "e6ofmn" + }, + { + "modelName": "DEEBOT X2 COMBO", + "deviceClass": "e6rcnf", + "deviceClassLink": "e6ofmn" + }, + { + "modelName": "DEEBOT X2 PRO OMNI", + "deviceClass": "ip3mmy", + "deviceClassLink": "e6ofmn" } ]