From c381e146a21b293229a4030f2ddcb78218152839 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 08:00:55 +0200 Subject: [PATCH 01/35] started implementation of complete automower API Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 75 ++++++ .../internal/bridge/AutomowerBridge.java | 58 +++++ .../automowerconnect/AutomowerConnectApi.java | 13 + .../rest/api/automowerconnect/dto/Action.java | 22 ++ .../api/automowerconnect/dto/Calendar.java | 4 + .../automowerconnect/dto/CalendarTask.java | 36 +++ .../automowerconnect/dto/Capabilities.java | 44 ++++ .../api/automowerconnect/dto/Headlight.java | 28 ++ .../automowerconnect/dto/HeadlightMode.java | 23 ++ .../automowerconnect/dto/InactiveReason.java | 22 ++ .../api/automowerconnect/dto/MowerApp.java | 28 +- .../automowerconnect/dto/MowerCalendar.java | 37 +++ .../dto/MowerCalendardRequest.java | 28 ++ .../api/automowerconnect/dto/MowerData.java | 50 +++- .../api/automowerconnect/dto/Planner.java | 10 + .../automowerconnect/dto/PlannerOverride.java | 7 +- .../dto/RestrictedReason.java | 5 +- .../api/automowerconnect/dto/Settings.java | 37 +++ .../api/automowerconnect/dto/Statistics.java | 91 +++++++ .../api/automowerconnect/dto/StayOutZone.java | 46 ++++ .../automowerconnect/dto/StayOutZones.java | 40 +++ .../api/automowerconnect/dto/WorkArea.java | 73 ++++++ .../internal/things/AutomowerHandler.java | 103 +++++++- .../resources/OH-INF/thing/thing-types.xml | 239 ++++++++++++++++++ 24 files changed, 1100 insertions(+), 19 deletions(-) create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index aa140b49e4209..4ba1d4dca204c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -40,14 +40,30 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATUS_NAME = GROUP_STATUS + "name"; public static final String CHANNEL_STATUS_MODE = GROUP_STATUS + "mode"; public static final String CHANNEL_STATUS_ACTIVITY = GROUP_STATUS + "activity"; + public static final String CHANNEL_STATUS_INACTIVE_REASON = GROUP_STATUS + "inactive-reason"; public static final String CHANNEL_STATUS_STATE = GROUP_STATUS + "state"; public static final String CHANNEL_STATUS_LAST_UPDATE = GROUP_STATUS + "last-update"; public static final String CHANNEL_STATUS_BATTERY = GROUP_STATUS + "battery"; public static final String CHANNEL_STATUS_ERROR_CODE = GROUP_STATUS + "error-code"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; + public static final String CHANNEL_STATUS_ERROR_CONFIRMABLE = GROUP_STATUS + "error-confirmable"; public static final String CHANNEL_PLANNER_NEXT_START = GROUP_STATUS + "planner-next-start"; public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_STATUS + "planner-override-action"; + public static final String CHANNEL_PLANNER_RESTRICTED_REASON = GROUP_STATUS + "planner-restricted-reason"; + public static final String CHANNEL_PLANNER_EXTERNAL_REASON = GROUP_STATUS + "planner-external-reason"; public static final String CHANNEL_CALENDAR_TASKS = GROUP_STATUS + "calendar-tasks"; + public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_STATUS + "setting-cutting-height"; + public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_STATUS + "setting-headlight-mode"; + public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATUS + + "stat-cutting-blade-usage-time"; + public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATUS + + "stat-number-of-charging-cycles"; + public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATUS + "stat-number-of-collisions"; + public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATUS + "stat-total-charging-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATUS + "stat-total-cutting-time"; + public static final String CHANNEL_STATISTIC_TOTAL_DRIVEN_DISTANCE = GROUP_STATUS + "stat-total-driven-distance"; + public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATUS + "stat-total-running-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATUS + "stat-total-searching-time"; // Position Channels ids public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly @@ -73,6 +89,60 @@ public class AutomowerBindingConstants { GROUP_POSITIONS + "position46", GROUP_POSITIONS + "position47", GROUP_POSITIONS + "position48", GROUP_POSITIONS + "position49", GROUP_POSITIONS + "position50")); + // Stayout Zones Channels ids + public static final String GROUP_STAYOUTZONES = ""; // no channel group in use at the moment, we'll possibly + // introduce + // this in a future release + public static final String CHANNEL_STAYOUTZONES_DIRTY = GROUP_STAYOUTZONES + "dirty"; + public static final ArrayList CHANNEL_STAYOUTZONES = new ArrayList<>(List.of( + GROUP_STAYOUTZONES + "zone01-id", GROUP_STAYOUTZONES + "zone01-name", GROUP_STAYOUTZONES + "zone01-enabled", + GROUP_STAYOUTZONES + "zone02-id", GROUP_STAYOUTZONES + "zone02-name", GROUP_STAYOUTZONES + "zone02-enabled", + GROUP_STAYOUTZONES + "zone03-id", GROUP_STAYOUTZONES + "zone03-name", GROUP_STAYOUTZONES + "zone03-enabled", + GROUP_STAYOUTZONES + "zone04-id", GROUP_STAYOUTZONES + "zone04-name", GROUP_STAYOUTZONES + "zone04-enabled", + GROUP_STAYOUTZONES + "zone05-id", GROUP_STAYOUTZONES + "zone05-name", GROUP_STAYOUTZONES + "zone05-enabled", + GROUP_STAYOUTZONES + "zone06-id", GROUP_STAYOUTZONES + "zone06-name", GROUP_STAYOUTZONES + "zone06-enabled", + GROUP_STAYOUTZONES + "zone07-id", GROUP_STAYOUTZONES + "zone07-name", GROUP_STAYOUTZONES + "zone07-enabled", + GROUP_STAYOUTZONES + "zone08-id", GROUP_STAYOUTZONES + "zone08-name", GROUP_STAYOUTZONES + "zone08-enabled", + GROUP_STAYOUTZONES + "zone09-id", GROUP_STAYOUTZONES + "zone09-name", GROUP_STAYOUTZONES + "zone09-enabled", + GROUP_STAYOUTZONES + "zone10-id", GROUP_STAYOUTZONES + "zone10-name", + GROUP_STAYOUTZONES + "zone10-enabled")); + + // Work Areas Channels ids + public static final String GROUP_WORKAREAS = ""; // no channel group in use at the moment, we'll possibly + // introduce + // this in a future release + public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>( + List.of(GROUP_WORKAREAS + "workareas01-id", GROUP_WORKAREAS + "workareas01-name", + GROUP_WORKAREAS + "workareas01-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas02-id", GROUP_WORKAREAS + "workareas02-name", + GROUP_WORKAREAS + "workareas02-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas03-id", GROUP_WORKAREAS + "workareas03-name", + GROUP_WORKAREAS + "workareas03-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas04-id", GROUP_WORKAREAS + "workareas04-name", + GROUP_WORKAREAS + "workareas04-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas05-id", GROUP_WORKAREAS + "workareas05-name", + GROUP_WORKAREAS + "workareas05-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas06-id", GROUP_WORKAREAS + "workareas06-name", + GROUP_WORKAREAS + "workareas06-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas07-id", GROUP_WORKAREAS + "workareas07-name", + GROUP_WORKAREAS + "workareas07-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas08-id", GROUP_WORKAREAS + "workareas08-name", + GROUP_WORKAREAS + "workareas08-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas09-id", GROUP_WORKAREAS + "workareas09-name", + GROUP_WORKAREAS + "workareas09-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas10-id", GROUP_WORKAREAS + "workareas10-name", + GROUP_WORKAREAS + "workareas10-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", + GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed")); + // Command Channel ids public static final String GROUP_COMMANDS = ""; // no channel group in use at the moment, we'll possibly introduce // this in a future release @@ -88,4 +158,9 @@ public class AutomowerBindingConstants { public static final String AUTOMOWER_NAME = "mowerName"; public static final String AUTOMOWER_MODEL = "mowerModel"; public static final String AUTOMOWER_SERIAL_NUMBER = "mowerSerialNumber"; + public static final String AUTOMOWER_CAN_CONFIRM_ERROR = "mowerCanConfirmError"; + public static final String AUTOMOWER_HAS_HEADLIGHTS = "mowerHasHeadlights"; + public static final String AUTOMOWER_HAS_POSITION = "mowerHasPosition"; + public static final String AUTOMOWER_HAS_STAY_OUT_ZONES = "mowerHasStayOutZones"; + public static final String AUTOMOWER_HAS_WORK_AREAS = "mowerHasWorkAreas"; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index db261562f8726..25e8a31a72b17 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -14,12 +14,18 @@ import java.io.IOException; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ScheduledExecutorService; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.automower.internal.rest.api.automowerconnect.AutomowerConnectApi; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Calendar; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendar; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendardRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommand; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; @@ -30,6 +36,13 @@ import org.openhab.core.auth.client.oauth2.OAuthClientService; import org.openhab.core.auth.client.oauth2.OAuthException; import org.openhab.core.auth.client.oauth2.OAuthResponseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; /** * The {@link AutomowerBridge} allows the communication to the various Husqvarna rest apis like the @@ -44,6 +57,9 @@ public class AutomowerBridge { private final AutomowerConnectApi automowerApi; + private final Logger logger = LoggerFactory.getLogger(AutomowerBridge.class); + private Gson gson = new Gson(); + public AutomowerBridge(OAuthClientService authService, String appKey, HttpClient httpClient, ScheduledExecutorService scheduler) { this.authService = authService; @@ -103,4 +119,46 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma request.setData(mowerCommand); automowerApi.sendCommand(appKey, authenticate().getAccessToken(), id, request); } + + /** + * Sends a calendar to the automower + * + * @param id The id of the mower + * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) + * as provided when reading the channel + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerCalendar(String id, String calendar) throws AutomowerCommunicationException { + List tasks = new ArrayList<>(); + + JsonArray calendarJson = new Gson().fromJson(calendar, JsonArray.class); + for (JsonElement task : calendarJson) { + CalendarTask calendarTask = new CalendarTask(); + JsonObject taskObj = task.getAsJsonObject(); + calendarTask.setStart(taskObj.get("start").getAsInt()); + calendarTask.setDuration(taskObj.get("duration").getAsInt()); + calendarTask.setMonday(taskObj.get("monday").getAsBoolean()); + calendarTask.setTuesday(taskObj.get("tuesday").getAsBoolean()); + calendarTask.setWednesday(taskObj.get("wednesday").getAsBoolean()); + calendarTask.setThursday(taskObj.get("thursday").getAsBoolean()); + calendarTask.setFriday(taskObj.get("friday").getAsBoolean()); + calendarTask.setSaturday(taskObj.get("saturday").getAsBoolean()); + calendarTask.setSunday(taskObj.get("sunday").getAsBoolean()); + tasks.add(calendarTask); + } + + Calendar cal = new Calendar(); + cal.setTasks(tasks); + + MowerCalendar mowerCalendar = new MowerCalendar(); + mowerCalendar.setType("calendar"); + mowerCalendar.setAttributes(cal); + + MowerCalendardRequest request = new MowerCalendardRequest(); + request.setData(mowerCalendar); + + logger.debug("request '{}'", gson.toJson(request)); + + automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, request); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index f6695a69439c7..51c0b86d85c01 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.openhab.binding.automower.internal.rest.api.HusqvarnaApi; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCalendardRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; @@ -78,6 +79,18 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ checkForError(response, response.getStatus()); } + public void sendCalendar(String appKey, String token, String id, MowerCalendardRequest calendar) + throws AutomowerCommunicationException { + final Request request = getHttpClient().newRequest(getBaseUrl() + "/mowers/" + id + "/calendar"); + request.method(HttpMethod.POST); + + request.content(new StringContentProvider(gson.toJson(calendar))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java new file mode 100644 index 0000000000000..ba1db02fb481d --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public enum Action { + NOT_ACTIVE, + FORCE_PARK, + FORCE_MOW +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java index ef4c35d670365..5a28b217ab372 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Calendar.java @@ -25,4 +25,8 @@ public class Calendar { public List getTasks() { return tasks; } + + public void setTasks(List tasks) { + this.tasks = tasks; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 87733f507ccb5..5996854e45483 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -68,4 +68,40 @@ public Boolean getSaturday() { public Boolean getSunday() { return sunday; } + + public void setStart(Integer start) { + this.start = start; + } + + public void setDuration(Integer duration) { + this.duration = duration; + } + + public void setMonday(Boolean monday) { + this.monday = monday; + } + + public void setTuesday(Boolean tuesday) { + this.tuesday = tuesday; + } + + public void setWednesday(Boolean wednesday) { + this.wednesday = wednesday; + } + + public void setThursday(Boolean thursday) { + this.thursday = thursday; + } + + public void setFriday(Boolean friday) { + this.friday = friday; + } + + public void setSaturday(Boolean saturday) { + this.saturday = saturday; + } + + public void setSunday(Boolean sunday) { + this.sunday = sunday; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java new file mode 100644 index 0000000000000..8a7c4efe841e5 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class Capabilities { + private Boolean canConfirmError; + private Boolean headlights; + private Boolean position; + private Boolean stayOutZones; + private Boolean workAreas; + + public Boolean canConfirmError() { + return canConfirmError; + } + + public Boolean hasHeadlights() { + return headlights; + } + + public Boolean hasPosition() { + return position; + } + + public Boolean hasStayOutZones() { + return stayOutZones; + } + + public Boolean hasWorkAreas() { + return workAreas; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java new file mode 100644 index 0000000000000..bd4d597c15b9e --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class Headlight { + private HeadlightMode mode; + + public HeadlightMode getHeadlightMode() { + return mode; + } + + public void setHeadlightMode(HeadlightMode mode) { + this.mode = mode; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java new file mode 100644 index 0000000000000..d32008c79eff8 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public enum HeadlightMode { + ALWAYS_ON, + ALWAYS_OFF, + EVENING_ONLY, + EVENING_AND_NIGHT +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java new file mode 100644 index 0000000000000..44d44cbbcc16c --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public enum InactiveReason { + NONE, + PLANNING, + SEARCHING_FOR_SATELLITES +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java index 0ec992d5dc053..66d122a51411c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java @@ -18,10 +18,12 @@ public class MowerApp { private Mode mode; private Activity activity; + private InactiveReason inactiveReason; private State state; - + private long workAreaId; private int errorCode; private long errorCodeTimestamp; + private Boolean isErrorConfirmable; public Mode getMode() { return mode; @@ -39,6 +41,14 @@ public void setActivity(Activity activity) { this.activity = activity; } + public InactiveReason getInactiveReason() { + return inactiveReason; + } + + public void setInactiveReason(InactiveReason inactiveReason) { + this.inactiveReason = inactiveReason; + } + public State getState() { return state; } @@ -47,6 +57,14 @@ public void setState(State state) { this.state = state; } + public long getWorkAreaId() { + return workAreaId; + } + + public void setWorkAreaId(long workAreaId) { + this.workAreaId = workAreaId; + } + public int getErrorCode() { return errorCode; } @@ -62,4 +80,12 @@ public long getErrorCodeTimestamp() { public void setErrorCodeTimestamp(long errorCodeTimestamp) { this.errorCodeTimestamp = errorCodeTimestamp; } + + public Boolean getIsErrorConfirmable() { + return isErrorConfirmable; + } + + public void setIsErrorConfirmable(Boolean isErrorConfirmable) { + this.isErrorConfirmable = isErrorConfirmable; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java new file mode 100644 index 0000000000000..f7b20c5000661 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class MowerCalendar { + private String type; + private Calendar attributes; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Calendar getAttributes() { + return attributes; + } + + public void setAttributes(Calendar attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java new file mode 100644 index 0000000000000..8b28b465dbcf2 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class MowerCalendardRequest { + private MowerCalendar data; + + public MowerCalendar getData() { + return data; + } + + public void setData(MowerCalendar data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java index 3f861c33fee0d..1ed50ce502ce9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerData.java @@ -13,6 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; import java.util.ArrayList; +import java.util.List; /** * @author Markus Pfleger - Initial contribution @@ -20,11 +21,16 @@ public class MowerData { private System system; private Battery battery; + private Capabilities capabilities; private MowerApp mower; private Calendar calendar; private Planner planner; private Metadata metadata; - private ArrayList positions = new ArrayList<>(); + private List positions = new ArrayList<>(); + private Settings settings; + private Statistics statistics; + private StayOutZones stayOutZones; + private List workAreas = new ArrayList<>(); public System getSystem() { return system; @@ -42,6 +48,14 @@ public void setBattery(Battery battery) { this.battery = battery; } + public Capabilities getCapabilities() { + return capabilities; + } + + public void setCapabilities(Capabilities capabilities) { + this.capabilities = capabilities; + } + public MowerApp getMower() { return mower; } @@ -78,11 +92,43 @@ public void addPosition(Position position) { this.positions.add(position); } - public ArrayList getPositions() { + public List getPositions() { return this.positions; } public Position getLastPosition() { return !this.positions.isEmpty() ? this.positions.get(0) : null; } + + public Settings getSettings() { + return settings; + } + + public void setSettings(Settings settings) { + this.settings = settings; + } + + public Statistics getStatistics() { + return statistics; + } + + public void setStatistics(Statistics statistics) { + this.statistics = statistics; + } + + public StayOutZones getStayOutZones() { + return stayOutZones; + } + + public void setStayOutZones(StayOutZones stayOutZones) { + this.stayOutZones = stayOutZones; + } + + public List getWorkAreas() { + return workAreas; + } + + public void setWorkAreas(List workAreas) { + this.workAreas = workAreas; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java index 6c5c6b74975f4..9ade8e538ceef 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java @@ -20,6 +20,7 @@ public class Planner { private long nextStartTimestamp; private RestrictedReason restrictedReason; private PlannerOverride override; + private int externalReason; public long getNextStartTimestamp() { return nextStartTimestamp; @@ -47,4 +48,13 @@ public Planner setOverride(PlannerOverride override) { this.override = override; return this; } + + public int getExternalReason() { + return externalReason; + } + + public Planner setExternalReason(int externalReason) { + this.externalReason = externalReason; + return this; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java index 915837b3e5090..77b6269258593 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/PlannerOverride.java @@ -16,14 +16,13 @@ * @author Marcin Czeczko - Initial contribution */ public class PlannerOverride { - private String action; + private Action action; - public String getAction() { + public Action getAction() { return action; } - public PlannerOverride setAction(String action) { + public void setAction(Action action) { this.action = action; - return this; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java index ac3002ec3d842..64c9c2e5bb60d 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/RestrictedReason.java @@ -21,5 +21,8 @@ public enum RestrictedReason { PARK_OVERRIDE, SENSOR, DAILY_LIMIT, - NOT_APPLICABLE + FOTA, + FROST, + ALL_WORK_AREAS_COMPLETED, + EXTERNAL } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java new file mode 100644 index 0000000000000..678e406a2bcd6 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class Settings { + private int cuttingHeight; + private Headlight headlight; + + public int getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(int cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public Headlight getHeadlight() { + return headlight; + } + + public void setHeadlight(Headlight headlight) { + this.headlight = headlight; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java new file mode 100644 index 0000000000000..cd06909cc7860 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class Statistics { + private long cuttingBladeUsageTime; + private int numberOfChargingCycles; + private int numberOfCollisions; + private long totalChargingTime; + private long totalCuttingTime; + private long totalDrivenDistance; + private long totalRunningTime; + private long totalSearchingTime; + + public long getCuttingBladeUsageTime() { + return cuttingBladeUsageTime; + } + + public void setCuttingBladeUsageTime(long cuttingBladeUsageTime) { + this.cuttingBladeUsageTime = cuttingBladeUsageTime; + } + + public int getNumberOfChargingCycles() { + return numberOfChargingCycles; + } + + public void setNumberOfChargingCycles(int numberOfChargingCycles) { + this.numberOfChargingCycles = numberOfChargingCycles; + } + + public int getNumberOfCollisions() { + return numberOfCollisions; + } + + public void setNumberOfCollisions(int numberOfCollisions) { + this.numberOfCollisions = numberOfCollisions; + } + + public long getTotalChargingTime() { + return totalChargingTime; + } + + public void setTotalChargingTime(long totalChargingTime) { + this.totalChargingTime = totalChargingTime; + } + + public long getTotalCuttingTime() { + return totalCuttingTime; + } + + public void setTotalCuttingTime(long totalCuttingTime) { + this.totalCuttingTime = totalCuttingTime; + } + + public long getTotalDrivenDistance() { + return totalDrivenDistance; + } + + public void setTotalDrivenDistance(long totalDrivenDistance) { + this.totalDrivenDistance = totalDrivenDistance; + } + + public long getTotalRunningTime() { + return totalRunningTime; + } + + public void setTotalRunningTime(long totalRunningTime) { + this.totalRunningTime = totalRunningTime; + } + + public long getTotalSearchingTime() { + return totalSearchingTime; + } + + public void setTotalSearchingTime(long totalSearchingTime) { + this.totalSearchingTime = totalSearchingTime; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java new file mode 100644 index 0000000000000..f21d893e9b77e --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class StayOutZone { + private String id; + private String name; + private Boolean enabled; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabed(Boolean enabled) { + this.enabled = enabled; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java new file mode 100644 index 0000000000000..8a58864a4bdb8 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class StayOutZones { + private Boolean dirty; + private List zones = new ArrayList<>(); + + public Boolean isDirty() { + return dirty; + } + + public void setDirty(Boolean dirty) { + this.dirty = dirty; + } + + public List getZones() { + return zones; + } + + public void setZones(List zones) { + this.zones = zones; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java new file mode 100644 index 0000000000000..c8b55623d9527 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class WorkArea { + private long workAreaId; + private String name; + private int cuttingHeight; + private Boolean enabled; + private int progress; // Only available for EPOS mowers and systematic mowing work areas. + private long lastTimeCompleted; + + public long getWorkAreaId() { + return workAreaId; + } + + public void setWorkAreaId(long workAreaId) { + this.workAreaId = workAreaId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(int cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + public long getLastTimeCompleted() { + return lastTimeCompleted; + } + + public void setLastTimeCompleted(long lastTimeCompleted) { + this.lastTimeCompleted = lastTimeCompleted; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index b0a87a253cd4b..f308ee78d5ba1 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -16,8 +16,8 @@ import java.time.Instant; import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -39,9 +39,11 @@ import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.PointType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -107,6 +109,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { logger.debug("Refreshing channel '{}'", channelUID); refreshChannels(channelUID); + } else if (CHANNEL_CALENDAR_TASKS.equals(channelUID.getId())) { + logger.debug("Sending calendar '{}'", command); + sendAutomowerCalendar(command.toString()); } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -269,6 +274,29 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM updateAutomowerState(); } + /** + * Sends a calendar to the automower + * + * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) + * as provided when reading the channel + */ + public void sendAutomowerCalendar(String calendar) { + logger.debug("Sending calendar '{}'", calendar); + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendar(id, calendar); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } @@ -278,6 +306,8 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATUS_NAME, new StringType(mower.getAttributes().getSystem().getName())); updateState(CHANNEL_STATUS_MODE, new StringType(mower.getAttributes().getMower().getMode().name())); updateState(CHANNEL_STATUS_ACTIVITY, new StringType(mower.getAttributes().getMower().getActivity().name())); + updateState(CHANNEL_STATUS_INACTIVE_REASON, + new StringType(mower.getAttributes().getMower().getInactiveReason().name())); if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); @@ -300,6 +330,9 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, new DateTimeType(toZonedDateTime(errorCodeTimestamp))); } + updateState(CHANNEL_STATUS_ERROR_CONFIRMABLE, + OnOffType.from(mower.getAttributes().getMower().getIsErrorConfirmable() == true)); + long nextStartTimestamp = mower.getAttributes().getPlanner().getNextStartTimestamp(); // If next start timestamp is 0 it means the mower should start now, so using current timestamp if (nextStartTimestamp == 0L) { @@ -308,15 +341,48 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_PLANNER_NEXT_START, new DateTimeType(toZonedDateTime(nextStartTimestamp))); } updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, - new StringType(mower.getAttributes().getPlanner().getOverride().getAction())); - + new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, + new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, + new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); updateState(CHANNEL_CALENDAR_TASKS, new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); + updateState(CHANNEL_SETTING_CUTTING_HEIGHT, + new DecimalType(mower.getAttributes().getSettings().getCuttingHeight())); + updateState(CHANNEL_SETTING_HEADLIGHT_MODE, + new StringType(mower.getAttributes().getSettings().getHeadlight().getHeadlightMode().name())); + logger.warn("mower.getAttributes().getStatistics().getCuttingBladeUsageTime(): {}, QuantityType(): {}", + mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), + new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND) + .toString()); + + updateState(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES, + new DecimalType(mower.getAttributes().getStatistics().getNumberOfChargingCycles())); + updateState(CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS, + new DecimalType(mower.getAttributes().getStatistics().getNumberOfCollisions())); + updateState(CHANNEL_STATISTIC_TOTAL_CHARGING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalChargingTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalCuttingTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_DRIVEN_DISTANCE, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalDrivenDistance(), SIUnits.METRE)); + logger.warn("mower.getAttributes().getStatistics().getTotalDrivenDistance(): {}, QuantityType(): {}", + mower.getAttributes().getStatistics().getTotalDrivenDistance(), + new QuantityType<>(mower.getAttributes().getStatistics().getTotalDrivenDistance(), SIUnits.METRE) + .toString()); + updateState(CHANNEL_STATISTIC_TOTAL_RUNNING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalRunningTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalSearchingTime(), Units.SECOND)); + updateState(LAST_POSITION, new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); - ArrayList positions = mower.getAttributes().getPositions(); + List positions = mower.getAttributes().getPositions(); for (int i = 0; i < positions.size(); i++) { updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), new DecimalType(positions.get(i).getLongitude()))); @@ -330,14 +396,29 @@ private void initializeProperties(@Nullable Mower mower) { properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); - if (mower.getAttributes() != null && mower.getAttributes().getSystem() != null) { - properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, - mower.getAttributes().getSystem().getSerialNumber()); - properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); - properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); - } + if (mower.getAttributes() != null) { + if (mower.getAttributes().getSystem() != null) { + properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, + mower.getAttributes().getSystem().getSerialNumber()); + properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); + properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); + } - updateProperties(properties); + if (mower.getAttributes().getCapabilities() != null) { + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, + mower.getAttributes().getCapabilities().canConfirmError().toString()); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, + mower.getAttributes().getCapabilities().hasHeadlights().toString()); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, + mower.getAttributes().getCapabilities().hasPosition().toString()); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, + mower.getAttributes().getCapabilities().hasStayOutZones().toString()); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, + mower.getAttributes().getCapabilities().hasWorkAreas().toString()); + } else { + logger.warn("mower.getAttributes().getCapabilities() != null"); + } + } } /** diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index b039eeea8edfb..f5e16f8869de4 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -47,14 +47,28 @@ + + + + + + + + + + + + + + @@ -117,6 +131,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -124,6 +180,11 @@ N/A N/A N/A + N/A + N/A + N/A + N/A + N/A @@ -181,6 +242,19 @@ + + String + + The current reason for being inactive + + + + + + + + + String @@ -235,6 +309,13 @@ + + Switch + + If the mower has an Error Code this attribute states if the error is confirmable + + + DateTime @@ -250,6 +331,20 @@ + + String + + The channel providing an reason that restrics current planner operation. + + + + + Number + + The channel providing an external reason that restrics current planner operation. + + + String @@ -257,6 +352,76 @@ + + Number + + Prescaled cutting height, Range: 1-9 + + + + + String + + Information about headlights + + + + + Number:Time + + The time since the last reset of the cutting blade usage counter + + + + + Number + + Number of charging cycles + + + + + Number + + The total number of collisions + + + + + Number:Time + + Total charging time + + + + + Number:Time + + Total cutting time + + + + + Number:Length + + Total driven distance + + + + + Number:Time + + The total running time (the wheel motors have been running) + + + + + Number:Time + + The total searching time + + + Location @@ -264,6 +429,80 @@ + + + + Switch + + If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable + or disable a stay-out zone. + + + + + String + + Id of the Stayout zone + + + + + String + + The name of the Stayout zone + + + + + Switch + + If the Stayout zone is enabled, the Automower® will not access the zone + + + + + + Number + + Id of the Work Area + + + + + String + + Name of the work area + + + + + Number:Dimensionless + + Cutting height in percent. 0-100 + + + + + Switch + + If the work area is enabled or disabled + + + + + Number:Dimensionless + + The progress on a work area. EPOS mowers and systematic mowing work areas only. + + + + + DateTime + + Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. + + + Number From 1445a1f979505411cf3dc590ea2cd87efeca065c Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 09:25:48 +0200 Subject: [PATCH 02/35] workaround for bug in automower API (issue created) Signed-off-by: Michael Weger --- .../automower/internal/AutomowerBindingConstants.java | 2 +- .../rest/api/automowerconnect/dto/Statistics.java | 10 +++++----- .../automower/internal/things/AutomowerHandler.java | 8 ++++---- .../src/main/resources/OH-INF/thing/thing-types.xml | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 4ba1d4dca204c..64bbd41307e19 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -61,7 +61,7 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATUS + "stat-number-of-collisions"; public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATUS + "stat-total-charging-time"; public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATUS + "stat-total-cutting-time"; - public static final String CHANNEL_STATISTIC_TOTAL_DRIVEN_DISTANCE = GROUP_STATUS + "stat-total-driven-distance"; + public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATUS + "stat-total-drive-distance"; public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATUS + "stat-total-running-time"; public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATUS + "stat-total-searching-time"; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java index cd06909cc7860..3b9f1a0c8a913 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java @@ -21,7 +21,7 @@ public class Statistics { private int numberOfCollisions; private long totalChargingTime; private long totalCuttingTime; - private long totalDrivenDistance; + private long totalDriveDistance; // docu states totalDrivenDistance which does not work private long totalRunningTime; private long totalSearchingTime; @@ -65,12 +65,12 @@ public void setTotalCuttingTime(long totalCuttingTime) { this.totalCuttingTime = totalCuttingTime; } - public long getTotalDrivenDistance() { - return totalDrivenDistance; + public long getTotalDriveDistance() { + return totalDriveDistance; } - public void setTotalDrivenDistance(long totalDrivenDistance) { - this.totalDrivenDistance = totalDrivenDistance; + public void setTotalDriveDistance(long totalDriveDistance) { + this.totalDriveDistance = totalDriveDistance; } public long getTotalRunningTime() { diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index f308ee78d5ba1..3bd7f4ec99f35 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -368,11 +368,11 @@ private void updateChannelState(@Nullable Mower mower) { new QuantityType<>(mower.getAttributes().getStatistics().getTotalChargingTime(), Units.SECOND)); updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getTotalCuttingTime(), Units.SECOND)); - updateState(CHANNEL_STATISTIC_TOTAL_DRIVEN_DISTANCE, - new QuantityType<>(mower.getAttributes().getStatistics().getTotalDrivenDistance(), SIUnits.METRE)); + updateState(CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE, + new QuantityType<>(mower.getAttributes().getStatistics().getTotalDriveDistance(), SIUnits.METRE)); logger.warn("mower.getAttributes().getStatistics().getTotalDrivenDistance(): {}, QuantityType(): {}", - mower.getAttributes().getStatistics().getTotalDrivenDistance(), - new QuantityType<>(mower.getAttributes().getStatistics().getTotalDrivenDistance(), SIUnits.METRE) + mower.getAttributes().getStatistics().getTotalDriveDistance(), + new QuantityType<>(mower.getAttributes().getStatistics().getTotalDriveDistance(), SIUnits.METRE) .toString()); updateState(CHANNEL_STATISTIC_TOTAL_RUNNING_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getTotalRunningTime(), Units.SECOND)); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index f5e16f8869de4..a068088a9ad14 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -66,7 +66,7 @@ - + @@ -401,9 +401,9 @@ - + Number:Length - + Total driven distance From 3a68f0003f20660acae5989e2b84aeb0aea09147 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 16:32:22 +0200 Subject: [PATCH 03/35] WorkAreas and StayOutZones Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 45 ++++++---- .../internal/bridge/AutomowerBridge.java | 4 +- .../api/automowerconnect/dto/Battery.java | 6 +- .../automowerconnect/dto/CalendarTask.java | 12 +-- .../api/automowerconnect/dto/Planner.java | 12 +-- .../api/automowerconnect/dto/Settings.java | 6 +- .../api/automowerconnect/dto/WorkArea.java | 18 ++-- .../internal/things/AutomowerHandler.java | 74 ++++++++++++---- .../resources/OH-INF/thing/thing-types.xml | 88 ++++++++++++++++--- 9 files changed, 190 insertions(+), 75 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 64bbd41307e19..bd39d5bf9d8f3 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -115,33 +115,42 @@ public class AutomowerBindingConstants { List.of(GROUP_WORKAREAS + "workareas01-id", GROUP_WORKAREAS + "workareas01-name", GROUP_WORKAREAS + "workareas01-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas02-id", GROUP_WORKAREAS + "workareas02-name", - GROUP_WORKAREAS + "workareas02-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas02-cutting-height", GROUP_WORKAREAS + "workareas02-enabled", + GROUP_WORKAREAS + "workareas02-progress", GROUP_WORKAREAS + "workareas02-last-time-completed", + GROUP_WORKAREAS + "workareas03-id", GROUP_WORKAREAS + "workareas03-name", - GROUP_WORKAREAS + "workareas03-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas03-cutting-height", GROUP_WORKAREAS + "workareas03-enabled", + GROUP_WORKAREAS + "workareas03-progress", GROUP_WORKAREAS + "workareas03-last-time-completed", + GROUP_WORKAREAS + "workareas04-id", GROUP_WORKAREAS + "workareas04-name", - GROUP_WORKAREAS + "workareas04-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas04-cutting-height", GROUP_WORKAREAS + "workareas04-enabled", + GROUP_WORKAREAS + "workareas04-progress", GROUP_WORKAREAS + "workareas04-last-time-completed", + GROUP_WORKAREAS + "workareas05-id", GROUP_WORKAREAS + "workareas05-name", - GROUP_WORKAREAS + "workareas05-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas05-cutting-height", GROUP_WORKAREAS + "workareas05-enabled", + GROUP_WORKAREAS + "workareas05-progress", GROUP_WORKAREAS + "workareas05-last-time-completed", + GROUP_WORKAREAS + "workareas06-id", GROUP_WORKAREAS + "workareas06-name", - GROUP_WORKAREAS + "workareas06-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas06-cutting-height", GROUP_WORKAREAS + "workareas06-enabled", + GROUP_WORKAREAS + "workareas06-progress", GROUP_WORKAREAS + "workareas06-last-time-completed", + GROUP_WORKAREAS + "workareas07-id", GROUP_WORKAREAS + "workareas07-name", - GROUP_WORKAREAS + "workareas07-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas07-cutting-height", GROUP_WORKAREAS + "workareas07-enabled", + GROUP_WORKAREAS + "workareas07-progress", GROUP_WORKAREAS + "workareas07-last-time-completed", + GROUP_WORKAREAS + "workareas08-id", GROUP_WORKAREAS + "workareas08-name", - GROUP_WORKAREAS + "workareas08-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas08-cutting-height", GROUP_WORKAREAS + "workareas08-enabled", + GROUP_WORKAREAS + "workareas08-progress", GROUP_WORKAREAS + "workareas08-last-time-completed", + GROUP_WORKAREAS + "workareas09-id", GROUP_WORKAREAS + "workareas09-name", - GROUP_WORKAREAS + "workareas09-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + GROUP_WORKAREAS + "workareas09-cutting-height", GROUP_WORKAREAS + "workareas09-enabled", + GROUP_WORKAREAS + "workareas09-progress", GROUP_WORKAREAS + "workareas09-last-time-completed", + GROUP_WORKAREAS + "workareas10-id", GROUP_WORKAREAS + "workareas10-name", - GROUP_WORKAREAS + "workareas10-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed")); + GROUP_WORKAREAS + "workareas10-cutting-height", GROUP_WORKAREAS + "workareas10-enabled", + GROUP_WORKAREAS + "workareas10-progress", GROUP_WORKAREAS + "workareas10-last-time-completed")); // Command Channel ids public static final String GROUP_COMMANDS = ""; // no channel group in use at the moment, we'll possibly introduce diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 25e8a31a72b17..c5dfc2750e2b0 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -135,8 +135,8 @@ public void sendAutomowerCalendar(String id, String calendar) throws AutomowerCo for (JsonElement task : calendarJson) { CalendarTask calendarTask = new CalendarTask(); JsonObject taskObj = task.getAsJsonObject(); - calendarTask.setStart(taskObj.get("start").getAsInt()); - calendarTask.setDuration(taskObj.get("duration").getAsInt()); + calendarTask.setStart(taskObj.get("start").getAsShort()); + calendarTask.setDuration(taskObj.get("duration").getAsShort()); calendarTask.setMonday(taskObj.get("monday").getAsBoolean()); calendarTask.setTuesday(taskObj.get("tuesday").getAsBoolean()); calendarTask.setWednesday(taskObj.get("wednesday").getAsBoolean()); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java index a21cfa496302e..32dd54e1510af 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Battery.java @@ -16,13 +16,13 @@ * @author Markus Pfleger - Initial contribution */ public class Battery { - private int batteryPercent; + private byte batteryPercent; - public int getBatteryPercent() { + public byte getBatteryPercent() { return batteryPercent; } - public void setBatteryPercent(int batteryPercent) { + public void setBatteryPercent(byte batteryPercent) { this.batteryPercent = batteryPercent; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 5996854e45483..8ea233561871a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -19,12 +19,12 @@ public class CalendarTask { /** * Start time expressed in minutes after midnight. */ - private Integer start; + private Short start; /** * Duration time expressed in minutes */ - private Integer duration; + private Short duration; private Boolean monday; private Boolean tuesday; private Boolean wednesday; @@ -33,11 +33,11 @@ public class CalendarTask { private Boolean saturday; private Boolean sunday; - public Integer getStart() { + public Short getStart() { return start; } - public Integer getDuration() { + public Short getDuration() { return duration; } @@ -69,11 +69,11 @@ public Boolean getSunday() { return sunday; } - public void setStart(Integer start) { + public void setStart(Short start) { this.start = start; } - public void setDuration(Integer duration) { + public void setDuration(Short duration) { this.duration = duration; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java index 9ade8e538ceef..14293270b8e7c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Planner.java @@ -26,35 +26,31 @@ public long getNextStartTimestamp() { return nextStartTimestamp; } - public Planner setNextStartTimestamp(long nextStartTimestamp) { + public void setNextStartTimestamp(long nextStartTimestamp) { this.nextStartTimestamp = nextStartTimestamp; - return this; } public RestrictedReason getRestrictedReason() { return restrictedReason; } - public Planner setRestrictedReason(RestrictedReason restrictedReason) { + public void setRestrictedReason(RestrictedReason restrictedReason) { this.restrictedReason = restrictedReason; - return this; } public PlannerOverride getOverride() { return override; } - public Planner setOverride(PlannerOverride override) { + public void setOverride(PlannerOverride override) { this.override = override; - return this; } public int getExternalReason() { return externalReason; } - public Planner setExternalReason(int externalReason) { + public void setExternalReason(int externalReason) { this.externalReason = externalReason; - return this; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java index 678e406a2bcd6..10da3586af961 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java @@ -16,14 +16,14 @@ * @author Markus Pfleger - Initial contribution */ public class Settings { - private int cuttingHeight; + private byte cuttingHeight; private Headlight headlight; - public int getCuttingHeight() { + public byte getCuttingHeight() { return cuttingHeight; } - public void setCuttingHeight(int cuttingHeight) { + public void setCuttingHeight(byte cuttingHeight) { this.cuttingHeight = cuttingHeight; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java index c8b55623d9527..a87f4c2a2415e 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java @@ -18,10 +18,10 @@ public class WorkArea { private long workAreaId; private String name; - private int cuttingHeight; + private byte cuttingHeight; private Boolean enabled; - private int progress; // Only available for EPOS mowers and systematic mowing work areas. - private long lastTimeCompleted; + private Byte progress; // Only available for EPOS mowers and systematic mowing work areas. + private Long lastTimeCompleted; // Only available for EPOS mowers and systematic mowing work areas. public long getWorkAreaId() { return workAreaId; @@ -39,11 +39,11 @@ public void setName(String name) { this.name = name; } - public int getCuttingHeight() { + public byte getCuttingHeight() { return cuttingHeight; } - public void setCuttingHeight(int cuttingHeight) { + public void setCuttingHeight(byte cuttingHeight) { this.cuttingHeight = cuttingHeight; } @@ -55,19 +55,19 @@ public void setEnabled(Boolean enabled) { this.enabled = enabled; } - public int getProgress() { + public Byte getProgress() { return progress; } - public void setProgress(int progress) { + public void setProgress(Byte progress) { this.progress = progress; } - public long getLastTimeCompleted() { + public Long getLastTimeCompleted() { return lastTimeCompleted; } - public void setLastTimeCompleted(long lastTimeCompleted) { + public void setLastTimeCompleted(Long lastTimeCompleted) { this.lastTimeCompleted = lastTimeCompleted; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 3bd7f4ec99f35..27f44f088a33f 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -35,6 +35,8 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.State; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.StayOutZone; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.WorkArea; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.core.i18n.TimeZoneProvider; import org.openhab.core.library.types.DateTimeType; @@ -311,9 +313,19 @@ private void updateChannelState(@Nullable Mower mower) { if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(RestrictedReason.NONE.name())); + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); } else { updateState(CHANNEL_STATUS_STATE, new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, + new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); + if (mower.getAttributes().getPlanner().getRestrictedReason() != RestrictedReason.EXTERNAL) { + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); + } else { + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, + new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); + } } updateState(CHANNEL_STATUS_LAST_UPDATE, @@ -342,10 +354,6 @@ private void updateChannelState(@Nullable Mower mower) { } updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, - new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); - updateState(CHANNEL_PLANNER_EXTERNAL_REASON, - new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); updateState(CHANNEL_CALENDAR_TASKS, new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); @@ -353,10 +361,6 @@ private void updateChannelState(@Nullable Mower mower) { new DecimalType(mower.getAttributes().getSettings().getCuttingHeight())); updateState(CHANNEL_SETTING_HEADLIGHT_MODE, new StringType(mower.getAttributes().getSettings().getHeadlight().getHeadlightMode().name())); - logger.warn("mower.getAttributes().getStatistics().getCuttingBladeUsageTime(): {}, QuantityType(): {}", - mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), - new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND) - .toString()); updateState(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND)); @@ -370,10 +374,6 @@ private void updateChannelState(@Nullable Mower mower) { new QuantityType<>(mower.getAttributes().getStatistics().getTotalCuttingTime(), Units.SECOND)); updateState(CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE, new QuantityType<>(mower.getAttributes().getStatistics().getTotalDriveDistance(), SIUnits.METRE)); - logger.warn("mower.getAttributes().getStatistics().getTotalDrivenDistance(): {}, QuantityType(): {}", - mower.getAttributes().getStatistics().getTotalDriveDistance(), - new QuantityType<>(mower.getAttributes().getStatistics().getTotalDriveDistance(), SIUnits.METRE) - .toString()); updateState(CHANNEL_STATISTIC_TOTAL_RUNNING_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getTotalRunningTime(), Units.SECOND)); updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME, @@ -386,7 +386,50 @@ private void updateChannelState(@Nullable Mower mower) { for (int i = 0; i < positions.size(); i++) { updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), new DecimalType(positions.get(i).getLongitude()))); + } + updateState(CHANNEL_STAYOUTZONES_DIRTY, + OnOffType.from(mower.getAttributes().getStayOutZones().isDirty() == true)); + + List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); + int j = 0; + for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); + updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled() == true)); + } + // clear remaining channels + for (; j < CHANNEL_STAYOUTZONES.size(); j++) { + updateState(CHANNEL_STAYOUTZONES.get(j), UnDefType.NULL); + } + + List workAreas = mower.getAttributes().getWorkAreas(); + j = 0; + for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { + + //logger.warn("workAreas.size(): {}, getName(): {}", workAreas.size(), workAreas.get(i).getName()); + + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getWorkAreaId())); + updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workAreas.get(i).getName())); + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getCuttingHeight())); + updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workAreas.get(i).isEnabled() == true)); + if (workAreas.get(i).getProgress() != null) { + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getProgress())); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } + + if ((workAreas.get(i).getLastTimeCompleted() != null) + && (workAreas.get(i).getLastTimeCompleted() != 0)) { + updateState(CHANNEL_WORKAREAS.get(j++), + new DateTimeType(toZonedDateTime(workAreas.get(i).getLastTimeCompleted()))); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } + } + // clear remaining channels + for (; j < CHANNEL_WORKAREAS.size(); j++) { + updateState(CHANNEL_WORKAREAS.get(j), UnDefType.NULL); } } } @@ -403,8 +446,11 @@ private void initializeProperties(@Nullable Mower mower) { properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); } - if (mower.getAttributes().getCapabilities() != null) { + logger.warn("mower.getAttributes().getCapabilities(): {}, canConfirmError(): {}", + mower.getAttributes().getCapabilities(), + mower.getAttributes().getCapabilities().canConfirmError().toString()); + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, mower.getAttributes().getCapabilities().canConfirmError().toString()); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, @@ -415,8 +461,6 @@ private void initializeProperties(@Nullable Mower mower) { mower.getAttributes().getCapabilities().hasStayOutZones().toString()); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, mower.getAttributes().getCapabilities().hasWorkAreas().toString()); - } else { - logger.warn("mower.getAttributes().getCapabilities() != null"); } } } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index a068088a9ad14..f62915ff31e6b 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -69,6 +69,7 @@ + @@ -132,7 +133,6 @@ - @@ -172,7 +172,61 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -198,10 +252,8 @@ How often the current automower state should be polled in seconds - - String @@ -267,13 +319,17 @@ - + + + + + - + @@ -335,7 +391,19 @@ String The channel providing an reason that restrics current planner operation. - + + + + + + + + + + + + + @@ -430,7 +498,6 @@ - Switch @@ -496,9 +563,9 @@ - + DateTime - + Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. @@ -540,5 +607,4 @@ Park and pause the mower schedule until manual resume - From d78808463cc0f604831110dbdd48d0b9087a4c00 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 16:43:25 +0200 Subject: [PATCH 04/35] QuantityType instead of DecimalType Signed-off-by: Michael Weger --- .../automower/internal/things/AutomowerHandler.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 27f44f088a33f..50847329d2b08 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -407,14 +407,16 @@ private void updateChannelState(@Nullable Mower mower) { j = 0; for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { - //logger.warn("workAreas.size(): {}, getName(): {}", workAreas.size(), workAreas.get(i).getName()); + // logger.warn("workAreas.size(): {}, getName(): {}", workAreas.size(), workAreas.get(i).getName()); updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getWorkAreaId())); updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workAreas.get(i).getName())); - updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getCuttingHeight())); + updateState(CHANNEL_WORKAREAS.get(j++), + new QuantityType<>(workAreas.get(i).getCuttingHeight(), Units.PERCENT)); updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workAreas.get(i).isEnabled() == true)); if (workAreas.get(i).getProgress() != null) { - updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getProgress())); + updateState(CHANNEL_WORKAREAS.get(j++), + new QuantityType<>(workAreas.get(i).getProgress(), Units.PERCENT)); } else { updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); } From 4fa132dc4e5d87b464c4e55b3b7ba6744e9baf11 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 19:35:10 +0200 Subject: [PATCH 05/35] relative statistics and patterns Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 2 +- .../internal/AutomowerBindingConstants.java | 3 ++ .../internal/things/AutomowerHandler.java | 16 ++++++++++ .../resources/OH-INF/thing/thing-types.xml | 32 ++++++++++++++----- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index a9f14b184edeb..d5daa4af06a61 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -13,7 +13,7 @@ All Husqvarna Automower models with "Automower Connect" should be supported. It ## Discovery -Once the bridge is created and configured, openHAB will automatically discover all Automowers registered on your account. +Once the bridge is created and configured, OpenHab will automatically discover all Automowers registered on your account. ## Thing Configuration diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index bd39d5bf9d8f3..c8e23e27ea522 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -61,9 +61,12 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATUS + "stat-number-of-collisions"; public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATUS + "stat-total-charging-time"; public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATUS + "stat-total-cutting-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATUS + "stat-total-cutting-percent"; public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATUS + "stat-total-drive-distance"; public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATUS + "stat-total-running-time"; public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATUS + "stat-total-searching-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATUS + + "stat-total-searching-percent"; // Position Channels ids public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 50847329d2b08..a937b352c56bf 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -379,6 +379,22 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getTotalSearchingTime(), Units.SECOND)); + if (mower.getAttributes().getStatistics().getTotalRunningTime() != 0) { + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT, + new QuantityType<>( + (float) mower.getAttributes().getStatistics().getTotalCuttingTime() + / (float) mower.getAttributes().getStatistics().getTotalRunningTime() * 100.0, + Units.PERCENT)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, + new QuantityType<>( + (float) mower.getAttributes().getStatistics().getTotalSearchingTime() + / (float) mower.getAttributes().getStatistics().getTotalRunningTime() * 100.0, + Units.PERCENT)); + } else { + updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT, new QuantityType<>(0, Units.PERCENT)); + updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, new QuantityType<>(0, Units.PERCENT)); + } + updateState(LAST_POSITION, new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index f62915ff31e6b..069b3b8dd0c84 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -66,9 +66,11 @@ + + @@ -438,7 +440,7 @@ Number:Time The time since the last reset of the cutting blade usage counter - + @@ -459,35 +461,49 @@ Number:Time Total charging time - + Number:Time Total cutting time - + + + + + Number:Dimensionless + + Total cutting time in percent + Number:Length Total driven distance - + Number:Time The total running time (the wheel motors have been running) - + Number:Time The total searching time - + + + + + Number:Dimensionless + + The total searching time in percent + @@ -546,7 +562,7 @@ Number:Dimensionless Cutting height in percent. 0-100 - + @@ -560,7 +576,7 @@ Number:Dimensionless The progress on a work area. EPOS mowers and systematic mowing work areas only. - + From 0e7d284bf097dfa405c90d163f79ab9ab68abbc0 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 20:33:26 +0200 Subject: [PATCH 06/35] reverted logic Signed-off-by: Michael Weger --- .../internal/things/AutomowerHandler.java | 33 +++++++++++++------ .../resources/OH-INF/thing/thing-types.xml | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index a937b352c56bf..346878906dedb 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -313,21 +313,29 @@ private void updateChannelState(@Nullable Mower mower) { if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(RestrictedReason.NONE.name())); - updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); } else { updateState(CHANNEL_STATUS_STATE, new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, - new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); - if (mower.getAttributes().getPlanner().getRestrictedReason() != RestrictedReason.EXTERNAL) { - updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); - } else { - updateState(CHANNEL_PLANNER_EXTERNAL_REASON, - new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); - } } + /* + * if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { + * updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); + * updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(RestrictedReason.NONE.name())); + * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); + * } else { + * updateState(CHANNEL_STATUS_STATE, + * new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); + * updateState(CHANNEL_PLANNER_RESTRICTED_REASON, + * new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); + * if (mower.getAttributes().getPlanner().getRestrictedReason() != RestrictedReason.EXTERNAL) { + * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); + * } else { + * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, + * new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); + * } + * } + */ updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType(toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp()))); updateState(CHANNEL_STATUS_BATTERY, @@ -354,6 +362,11 @@ private void updateChannelState(@Nullable Mower mower) { } updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, + new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, + new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); + updateState(CHANNEL_CALENDAR_TASKS, new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 069b3b8dd0c84..dc47e298cad39 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -285,7 +285,7 @@ - + From 3858e8037fbb6c855a12de7e0d8915edb0c77391 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 22:48:28 +0200 Subject: [PATCH 07/35] full calendar read support Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 2 +- .../internal/AutomowerBindingConstants.java | 57 +++++- .../automowerconnect/dto/CalendarTask.java | 9 + .../internal/things/AutomowerHandler.java | 54 +++++- .../resources/OH-INF/thing/thing-types.xml | 174 +++++++++++++++++- 5 files changed, 276 insertions(+), 20 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index d5daa4af06a61..4ea290f20e3db 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -13,7 +13,7 @@ All Husqvarna Automower models with "Automower Connect" should be supported. It ## Discovery -Once the bridge is created and configured, OpenHab will automatically discover all Automowers registered on your account. +Once the bridge is created and configured, openHab will automatically discover all Automowers registered on your account. ## Thing Configuration diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index c8e23e27ea522..93952bc9641fb 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -51,7 +51,6 @@ public class AutomowerBindingConstants { public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_STATUS + "planner-override-action"; public static final String CHANNEL_PLANNER_RESTRICTED_REASON = GROUP_STATUS + "planner-restricted-reason"; public static final String CHANNEL_PLANNER_EXTERNAL_REASON = GROUP_STATUS + "planner-external-reason"; - public static final String CHANNEL_CALENDAR_TASKS = GROUP_STATUS + "calendar-tasks"; public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_STATUS + "setting-cutting-height"; public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_STATUS + "setting-headlight-mode"; public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATUS @@ -68,6 +67,62 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATUS + "stat-total-searching-percent"; + // Calendar Task Channels ids + public static final String GROUP_CALENDARTASKS = ""; // no channel group in use at the moment, we'll possibly + // introduce + // this in a future release + public static final ArrayList CHANNEL_CALENDARTASKS = new ArrayList<>(List.of( + GROUP_CALENDARTASKS + "calendartasks01-start", GROUP_CALENDARTASKS + "calendartasks01-duration", + GROUP_CALENDARTASKS + "calendartasks01-monday", GROUP_CALENDARTASKS + "calendartasks01-tuesday", + GROUP_CALENDARTASKS + "calendartasks01-wednesday", GROUP_CALENDARTASKS + "calendartasks01-thursday", + GROUP_CALENDARTASKS + "calendartasks01-friday", GROUP_CALENDARTASKS + "calendartasks01-saturday", + GROUP_CALENDARTASKS + "calendartasks01-sunday", GROUP_CALENDARTASKS + "calendartasks01-workAreaId", + GROUP_CALENDARTASKS + "calendartasks02-start", GROUP_CALENDARTASKS + "calendartasks02-duration", + GROUP_CALENDARTASKS + "calendartasks02-monday", GROUP_CALENDARTASKS + "calendartasks02-tuesday", + GROUP_CALENDARTASKS + "calendartasks02-wednesday", GROUP_CALENDARTASKS + "calendartasks02-thursday", + GROUP_CALENDARTASKS + "calendartasks02-friday", GROUP_CALENDARTASKS + "calendartasks02-saturday", + GROUP_CALENDARTASKS + "calendartasks02-sunday", GROUP_CALENDARTASKS + "calendartasks02-workAreaId", + GROUP_CALENDARTASKS + "calendartasks03-start", GROUP_CALENDARTASKS + "calendartasks03-duration", + GROUP_CALENDARTASKS + "calendartasks03-monday", GROUP_CALENDARTASKS + "calendartasks03-tuesday", + GROUP_CALENDARTASKS + "calendartasks03-wednesday", GROUP_CALENDARTASKS + "calendartasks03-thursday", + GROUP_CALENDARTASKS + "calendartasks03-friday", GROUP_CALENDARTASKS + "calendartasks03-saturday", + GROUP_CALENDARTASKS + "calendartasks03-sunday", GROUP_CALENDARTASKS + "calendartasks03-workAreaId", + GROUP_CALENDARTASKS + "calendartasks04-start", GROUP_CALENDARTASKS + "calendartasks04-duration", + GROUP_CALENDARTASKS + "calendartasks04-monday", GROUP_CALENDARTASKS + "calendartasks04-tuesday", + GROUP_CALENDARTASKS + "calendartasks04-wednesday", GROUP_CALENDARTASKS + "calendartasks04-thursday", + GROUP_CALENDARTASKS + "calendartasks04-friday", GROUP_CALENDARTASKS + "calendartasks04-saturday", + GROUP_CALENDARTASKS + "calendartasks04-sunday", GROUP_CALENDARTASKS + "calendartasks04-workAreaId", + GROUP_CALENDARTASKS + "calendartasks05-start", GROUP_CALENDARTASKS + "calendartasks05-duration", + GROUP_CALENDARTASKS + "calendartasks05-monday", GROUP_CALENDARTASKS + "calendartasks05-tuesday", + GROUP_CALENDARTASKS + "calendartasks05-wednesday", GROUP_CALENDARTASKS + "calendartasks05-thursday", + GROUP_CALENDARTASKS + "calendartasks05-friday", GROUP_CALENDARTASKS + "calendartasks05-saturday", + GROUP_CALENDARTASKS + "calendartasks05-sunday", GROUP_CALENDARTASKS + "calendartasks05-workAreaId", + GROUP_CALENDARTASKS + "calendartasks06-start", GROUP_CALENDARTASKS + "calendartasks06-duration", + GROUP_CALENDARTASKS + "calendartasks06-monday", GROUP_CALENDARTASKS + "calendartasks06-tuesday", + GROUP_CALENDARTASKS + "calendartasks06-wednesday", GROUP_CALENDARTASKS + "calendartasks06-thursday", + GROUP_CALENDARTASKS + "calendartasks06-friday", GROUP_CALENDARTASKS + "calendartasks06-saturday", + GROUP_CALENDARTASKS + "calendartasks06-sunday", GROUP_CALENDARTASKS + "calendartasks06-workAreaId", + GROUP_CALENDARTASKS + "calendartasks07-start", GROUP_CALENDARTASKS + "calendartasks07-duration", + GROUP_CALENDARTASKS + "calendartasks07-monday", GROUP_CALENDARTASKS + "calendartasks07-tuesday", + GROUP_CALENDARTASKS + "calendartasks07-wednesday", GROUP_CALENDARTASKS + "calendartasks07-thursday", + GROUP_CALENDARTASKS + "calendartasks07-friday", GROUP_CALENDARTASKS + "calendartasks07-saturday", + GROUP_CALENDARTASKS + "calendartasks07-sunday", GROUP_CALENDARTASKS + "calendartasks07-workAreaId", + GROUP_CALENDARTASKS + "calendartasks08-start", GROUP_CALENDARTASKS + "calendartasks08-duration", + GROUP_CALENDARTASKS + "calendartasks08-monday", GROUP_CALENDARTASKS + "calendartasks08-tuesday", + GROUP_CALENDARTASKS + "calendartasks08-wednesday", GROUP_CALENDARTASKS + "calendartasks08-thursday", + GROUP_CALENDARTASKS + "calendartasks08-friday", GROUP_CALENDARTASKS + "calendartasks08-saturday", + GROUP_CALENDARTASKS + "calendartasks08-sunday", GROUP_CALENDARTASKS + "calendartasks08-workAreaId", + GROUP_CALENDARTASKS + "calendartasks09-start", GROUP_CALENDARTASKS + "calendartasks09-duration", + GROUP_CALENDARTASKS + "calendartasks09-monday", GROUP_CALENDARTASKS + "calendartasks09-tuesday", + GROUP_CALENDARTASKS + "calendartasks09-wednesday", GROUP_CALENDARTASKS + "calendartasks09-thursday", + GROUP_CALENDARTASKS + "calendartasks09-friday", GROUP_CALENDARTASKS + "calendartasks09-saturday", + GROUP_CALENDARTASKS + "calendartasks09-sunday", GROUP_CALENDARTASKS + "calendartasks09-workAreaId", + GROUP_CALENDARTASKS + "calendartasks10-start", GROUP_CALENDARTASKS + "calendartasks10-duration", + GROUP_CALENDARTASKS + "calendartasks10-monday", GROUP_CALENDARTASKS + "calendartasks10-tuesday", + GROUP_CALENDARTASKS + "calendartasks10-wednesday", GROUP_CALENDARTASKS + "calendartasks10-thursday", + GROUP_CALENDARTASKS + "calendartasks10-friday", GROUP_CALENDARTASKS + "calendartasks10-saturday", + GROUP_CALENDARTASKS + "calendartasks10-sunday", GROUP_CALENDARTASKS + "calendartasks10-workAreaId")); + // Position Channels ids public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly // introduce diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 8ea233561871a..298912d3e3833 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -32,6 +32,7 @@ public class CalendarTask { private Boolean friday; private Boolean saturday; private Boolean sunday; + private int workAreaId; public Short getStart() { return start; @@ -69,6 +70,10 @@ public Boolean getSunday() { return sunday; } + public int getWorkAreaId() { + return workAreaId; + } + public void setStart(Short start) { this.start = start; } @@ -104,4 +109,8 @@ public void setSaturday(Boolean saturday) { public void setSunday(Boolean sunday) { this.sunday = sunday; } + + public void setWorkAreaId(int workAreaId) { + this.workAreaId = workAreaId; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 346878906dedb..384d80eb6a156 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -31,6 +31,7 @@ import org.openhab.binding.automower.internal.actions.AutomowerActions; import org.openhab.binding.automower.internal.bridge.AutomowerBridge; import org.openhab.binding.automower.internal.bridge.AutomowerBridgeHandler; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; @@ -63,8 +64,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; - /** * The {@link AutomowerHandler} is responsible for handling commands, which are * sent to one of the channels. @@ -90,7 +89,7 @@ public class AutomowerHandler extends BaseThingHandler { private @Nullable Mower mowerState; - private Gson gson = new Gson(); + // private Gson gson = new Gson(); private Runnable automowerPollingRunnable = () -> { Bridge bridge = getBridge(); @@ -111,9 +110,11 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { logger.debug("Refreshing channel '{}'", channelUID); refreshChannels(channelUID); - } else if (CHANNEL_CALENDAR_TASKS.equals(channelUID.getId())) { - logger.debug("Sending calendar '{}'", command); - sendAutomowerCalendar(command.toString()); + /* + * } else if (CHANNEL_CALENDAR_TASKS.equals(channelUID.getId())) { + * logger.debug("Sending calendar '{}'", command); + * sendAutomowerCalendar(command.toString()); + */ } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -367,9 +368,6 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); - updateState(CHANNEL_CALENDAR_TASKS, - new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); - updateState(CHANNEL_SETTING_CUTTING_HEIGHT, new DecimalType(mower.getAttributes().getSettings().getCuttingHeight())); updateState(CHANNEL_SETTING_HEADLIGHT_MODE, @@ -420,8 +418,44 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STAYOUTZONES_DIRTY, OnOffType.from(mower.getAttributes().getStayOutZones().isDirty() == true)); - List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); + /* + * updateState(CHANNEL_CALENDAR_TASKS, + * new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); + * + * - start + * - duration + * - monday + * - tuesday + * - wednesday + * - thursday + * - friday + * - saturday + * - sunday + */ + List calendarTasks = mower.getAttributes().getCalendar().getTasks(); int j = 0; + for (int i = 0; i < calendarTasks.size() && j < CHANNEL_CALENDARTASKS.size(); i++) { + updateState(CHANNEL_CALENDARTASKS.get(j++), + new QuantityType<>(calendarTasks.get(i).getStart(), Units.MINUTE)); + updateState(CHANNEL_CALENDARTASKS.get(j++), + new QuantityType<>(calendarTasks.get(i).getDuration(), Units.MINUTE)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getMonday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getTuesday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), + OnOffType.from(calendarTasks.get(i).getWednesday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getThursday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getFriday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSaturday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); + } + // clear remaining channels + for (; j < CHANNEL_CALENDARTASKS.size(); j++) { + updateState(CHANNEL_CALENDARTASKS.get(j), UnDefType.NULL); + } + + List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); + j = 0; for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index dc47e298cad39..fde9f0ad5f618 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -58,7 +58,6 @@ - @@ -72,6 +71,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -415,13 +525,6 @@ - - String - - The channel providing a JSON with the information about the planner tasks. - - - Number @@ -513,6 +616,61 @@ + + + Number:Time + + Start time relative to midnight + + + + Number:Time + + Duration time + + + + Switch + + Enabled on Mondays + + + Switch + + Enabled on Tuesdays + + + Switch + + Enabled on Wednesdays + + + Switch + + Enabled on Thursdays + + + Switch + + Enabled on Fridays + + + Switch + + Enabled on Saturdays + + + Switch + + Enabled on Sundays + + + Number + + Work Area Id mapped to this calendar + + + Switch From 7f1d520d27c546f21bfff3dbce4a777e34e96756 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 12 Oct 2024 23:55:21 +0200 Subject: [PATCH 08/35] finally ... Signed-off-by: Michael Weger --- bundles/org.openhab.binding.automower/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 4ea290f20e3db..a9f14b184edeb 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -13,7 +13,7 @@ All Husqvarna Automower models with "Automower Connect" should be supported. It ## Discovery -Once the bridge is created and configured, openHab will automatically discover all Automowers registered on your account. +Once the bridge is created and configured, openHAB will automatically discover all Automowers registered on your account. ## Thing Configuration From f74213b0016827e5868ea63cf9a6448f41c9c81e Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 14:07:54 +0200 Subject: [PATCH 09/35] new mowerZoneId config parameter workaround for ambiguous timezone handling of automower api Signed-off-by: Michael Weger --- .../things/AutomowerConfiguration.java | 6 ++ .../internal/things/AutomowerHandler.java | 57 +++++++++++++++---- .../resources/OH-INF/thing/thing-types.xml | 4 ++ 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java index 8b18d15c72181..10528a46699c2 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerConfiguration.java @@ -24,6 +24,7 @@ public class AutomowerConfiguration { public @Nullable String mowerId; public @Nullable Integer pollingInterval; + public @Nullable String mowerZoneId; @Nullable public String getMowerId() { @@ -41,4 +42,9 @@ public void setMowerId(String mowerId) { public void setPollingInterval(Integer pollingInterval) { this.pollingInterval = pollingInterval; } + + @Nullable + public String getMowerZoneId() { + return mowerZoneId; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 384d80eb6a156..db45ca2d63678 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -14,7 +14,9 @@ import static org.openhab.binding.automower.internal.AutomowerBindingConstants.*; +import java.time.DateTimeException; import java.time.Instant; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Collection; import java.util.List; @@ -80,6 +82,7 @@ public class AutomowerHandler extends BaseThingHandler { private final Logger logger = LoggerFactory.getLogger(AutomowerHandler.class); private final TimeZoneProvider timeZoneProvider; + private ZoneId mowerZoneId; private AtomicReference automowerId = new AtomicReference<>(NO_ID); private long lastQueryTimeMs = 0L; @@ -103,6 +106,7 @@ public class AutomowerHandler extends BaseThingHandler { public AutomowerHandler(Thing thing, TimeZoneProvider timeZoneProvider) { super(thing); this.timeZoneProvider = timeZoneProvider; + this.mowerZoneId = timeZoneProvider.getTimeZone(); // default initializer } @Override @@ -147,6 +151,17 @@ public void initialize() { AutomowerConfiguration currentConfig = getConfigAs(AutomowerConfiguration.class); final String configMowerId = currentConfig.getMowerId(); final Integer pollingIntervalS = currentConfig.getPollingInterval(); + final String configMowerZoneId = currentConfig.getMowerZoneId(); + if ((configMowerZoneId != null) && !configMowerZoneId.isBlank()) { + try { + mowerZoneId = ZoneId.of(configMowerZoneId); + } catch (DateTimeException e) { + logger.warn("Invalid configuration mowerZoneId: {}, Error: {}", mowerZoneId, e.getMessage()); + mowerZoneId = timeZoneProvider.getTimeZone(); // wrong config, use System TimeZone + } + } else { + mowerZoneId = timeZoneProvider.getTimeZone(); // not configured, use System TimeZone + } if (configMowerId == null) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, @@ -337,8 +352,13 @@ private void updateChannelState(@Nullable Mower mower) { * } * } */ - updateState(CHANNEL_STATUS_LAST_UPDATE, - new DateTimeType(toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp()))); + /* + * logger.warn("CHANNEL_STATUS_LAST_UPDATE: {}, zoneID(): {}", + * mower.getAttributes().getMetadata().getStatusTimestamp(), + * ZoneId.of("UTC").toString()); + */ + updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType( + toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp(), ZoneId.of("UTC")))); updateState(CHANNEL_STATUS_BATTERY, new QuantityType<>(mower.getAttributes().getBattery().getBatteryPercent(), Units.PERCENT)); @@ -348,7 +368,8 @@ private void updateChannelState(@Nullable Mower mower) { if (errorCodeTimestamp == 0L) { updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, UnDefType.NULL); } else { - updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, new DateTimeType(toZonedDateTime(errorCodeTimestamp))); + updateState(CHANNEL_STATUS_ERROR_TIMESTAMP, + new DateTimeType(toZonedDateTime(errorCodeTimestamp, mowerZoneId))); } updateState(CHANNEL_STATUS_ERROR_CONFIRMABLE, @@ -359,7 +380,12 @@ private void updateChannelState(@Nullable Mower mower) { if (nextStartTimestamp == 0L) { updateState(CHANNEL_PLANNER_NEXT_START, UnDefType.NULL); } else { - updateState(CHANNEL_PLANNER_NEXT_START, new DateTimeType(toZonedDateTime(nextStartTimestamp))); + /* + * logger.warn("CHANNEL_PLANNER_NEXT_START: {}, zoneID(): {}", nextStartTimestamp, + * timeZoneProvider.getTimeZone().toString()); + */ + updateState(CHANNEL_PLANNER_NEXT_START, + new DateTimeType(toZonedDateTime(nextStartTimestamp, mowerZoneId))); } updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); @@ -487,7 +513,7 @@ private void updateChannelState(@Nullable Mower mower) { if ((workAreas.get(i).getLastTimeCompleted() != null) && (workAreas.get(i).getLastTimeCompleted() != 0)) { updateState(CHANNEL_WORKAREAS.get(j++), - new DateTimeType(toZonedDateTime(workAreas.get(i).getLastTimeCompleted()))); + new DateTimeType(toZonedDateTime(workAreas.get(i).getLastTimeCompleted(), mowerZoneId))); } else { updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); } @@ -531,16 +557,23 @@ private void initializeProperties(@Nullable Mower mower) { } /** - * Converts timestamp returned by the Automower API into local time-zone. - * Timestamp returned by the API doesn't have offset and it always in the current time zone - it can be treated as - * UTC. - * Method builds a ZonedDateTime with same hour value but in the current system timezone. + * Converts timestamp returned by the Automower API into ZonedDateTime of the specified timezone. + * Timestamp returned by the API is a mix of local and UTC timezone. + * Method builds a valid ZonedDateTime object according to the provided ZoneId. * * @param timestamp - Automower API timestamp - * @return ZonedDateTime in system timezone + * @param zoneId - Timezone of the timestamp + * @return ZonedDateTime using provided timezone */ - private ZonedDateTime toZonedDateTime(long timestamp) { + private ZonedDateTime toZonedDateTime(long timestamp, ZoneId zoneId) { Instant timestampInstant = Instant.ofEpochMilli(timestamp); - return ZonedDateTime.ofInstant(timestampInstant, timeZoneProvider.getTimeZone()); + ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(timestampInstant, zoneId); + long timeDiff = zonedDateTime.getOffset().getTotalSeconds(); + ZonedDateTime convertedTimeInMowerTimeZone = zonedDateTime.minusSeconds(timeDiff); + logger.debug( + "toZonedDateTime() - timestamp: {}, zoneId: {}, zonedDateTime: {}, timeDiff: {}, convertedTimeInMowerTimeZone: {}", + timestamp, zoneId.toString(), zonedDateTime.toString(), timeDiff, + convertedTimeInMowerTimeZone.toString()); + return convertedTimeInMowerTimeZone; } } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index fde9f0ad5f618..e314534e6d91e 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -363,6 +363,10 @@ 600 How often the current automower state should be polled in seconds + + + Time zone of the mower (e.g. Europe/Berlin) + From eb132a50faebb2cc68d2b538ece710bd05f4dbe3 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 15:40:54 +0200 Subject: [PATCH 10/35] cleaned up time zone handling Signed-off-by: Michael Weger --- .../internal/things/AutomowerHandler.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index db45ca2d63678..719bc89656153 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -558,22 +558,14 @@ private void initializeProperties(@Nullable Mower mower) { /** * Converts timestamp returned by the Automower API into ZonedDateTime of the specified timezone. - * Timestamp returned by the API is a mix of local and UTC timezone. - * Method builds a valid ZonedDateTime object according to the provided ZoneId. + * Timestamp returned by the API is a mix of timezone specified via zoneId and UTC timezone. + * Method builds a valid ZonedDateTime object according to the provided zoneId parameter. * * @param timestamp - Automower API timestamp - * @param zoneId - Timezone of the timestamp + * @param zoneId - Intended timezone of the timestamp * @return ZonedDateTime using provided timezone */ private ZonedDateTime toZonedDateTime(long timestamp, ZoneId zoneId) { - Instant timestampInstant = Instant.ofEpochMilli(timestamp); - ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(timestampInstant, zoneId); - long timeDiff = zonedDateTime.getOffset().getTotalSeconds(); - ZonedDateTime convertedTimeInMowerTimeZone = zonedDateTime.minusSeconds(timeDiff); - logger.debug( - "toZonedDateTime() - timestamp: {}, zoneId: {}, zonedDateTime: {}, timeDiff: {}, convertedTimeInMowerTimeZone: {}", - timestamp, zoneId.toString(), zonedDateTime.toString(), timeDiff, - convertedTimeInMowerTimeZone.toString()); - return convertedTimeInMowerTimeZone; + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("UTC")).withZoneSameLocal(zoneId); } } From a38b63f09edffb23e52d6d0fcc2cc1a7f41aa838 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 20:48:52 +0200 Subject: [PATCH 11/35] implementation of CalendarTask POST Signed-off-by: Michael Weger --- .../internal/bridge/AutomowerBridge.java | 41 +++----- .../automowerconnect/AutomowerConnectApi.java | 17 +++- .../automowerconnect/dto/CalendarTask.java | 62 ++++++------ .../internal/things/AutomowerHandler.java | 99 +++++++++++-------- 4 files changed, 117 insertions(+), 102 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index c5dfc2750e2b0..33d1a50c74dc9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -40,9 +40,6 @@ import org.slf4j.LoggerFactory; import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; /** * The {@link AutomowerBridge} allows the communication to the various Husqvarna rest apis like the @@ -128,37 +125,31 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma * as provided when reading the channel * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerCalendar(String id, String calendar) throws AutomowerCommunicationException { - List tasks = new ArrayList<>(); - - JsonArray calendarJson = new Gson().fromJson(calendar, JsonArray.class); - for (JsonElement task : calendarJson) { - CalendarTask calendarTask = new CalendarTask(); - JsonObject taskObj = task.getAsJsonObject(); - calendarTask.setStart(taskObj.get("start").getAsShort()); - calendarTask.setDuration(taskObj.get("duration").getAsShort()); - calendarTask.setMonday(taskObj.get("monday").getAsBoolean()); - calendarTask.setTuesday(taskObj.get("tuesday").getAsBoolean()); - calendarTask.setWednesday(taskObj.get("wednesday").getAsBoolean()); - calendarTask.setThursday(taskObj.get("thursday").getAsBoolean()); - calendarTask.setFriday(taskObj.get("friday").getAsBoolean()); - calendarTask.setSaturday(taskObj.get("saturday").getAsBoolean()); - calendarTask.setSunday(taskObj.get("sunday").getAsBoolean()); - tasks.add(calendarTask); - } - Calendar cal = new Calendar(); - cal.setTasks(tasks); + /** + * Sends a calendar to the automower + * + * @param id The id of the mower + * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) + * as provided when reading the channel + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerCalendarTask(String id, Integer workAreaId, CalendarTask calendarTask) + throws AutomowerCommunicationException { + List tasks = new ArrayList<>(); + tasks.add(calendarTask); + Calendar calendar = new Calendar(); + calendar.setTasks(tasks); MowerCalendar mowerCalendar = new MowerCalendar(); mowerCalendar.setType("calendar"); - mowerCalendar.setAttributes(cal); + mowerCalendar.setAttributes(calendar); MowerCalendardRequest request = new MowerCalendardRequest(); request.setData(mowerCalendar); logger.debug("request '{}'", gson.toJson(request)); - automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, request); + automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, workAreaId, request); } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 51c0b86d85c01..e00d27a5cd0f8 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -30,6 +30,8 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.JsonSyntaxException; @@ -40,6 +42,8 @@ */ @NonNullByDefault public class AutomowerConnectApi extends HusqvarnaApi { + private final Logger logger = LoggerFactory.getLogger(AutomowerConnectApi.class); + public AutomowerConnectApi(HttpClient httpClient) { super(httpClient); } @@ -63,7 +67,6 @@ public MowerResult getMower(String appKey, String token, String mowerId) throws request.method(HttpMethod.GET); ContentResponse response = executeRequest(appKey, token, request); - return parseResponse(response, MowerResult.class); } @@ -79,9 +82,15 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ checkForError(response, response.getStatus()); } - public void sendCalendar(String appKey, String token, String id, MowerCalendardRequest calendar) + public void sendCalendar(String appKey, String token, String id, Integer workAreaId, MowerCalendardRequest calendar) throws AutomowerCommunicationException { - final Request request = getHttpClient().newRequest(getBaseUrl() + "/mowers/" + id + "/calendar"); + String url; + if (workAreaId == null) { + url = getBaseUrl() + "/mowers/" + id + "/calendar"; + } else { + url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId + "/calendar"; + } + final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.POST); request.content(new StringContentProvider(gson.toJson(calendar))); @@ -116,7 +125,7 @@ private T parseResponse(ContentResponse response, Class type) throws Auto int statusCode = response.getStatus(); checkForError(response, statusCode); - + logger.warn("response: {}", response.getContentAsString()); try { return gson.fromJson(response.getContentAsString(), type); } catch (JsonSyntaxException e) { diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 298912d3e3833..8e42d589c6281 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -19,98 +19,98 @@ public class CalendarTask { /** * Start time expressed in minutes after midnight. */ - private Short start; + private short start; /** * Duration time expressed in minutes */ - private Short duration; - private Boolean monday; - private Boolean tuesday; - private Boolean wednesday; - private Boolean thursday; - private Boolean friday; - private Boolean saturday; - private Boolean sunday; - private int workAreaId; - - public Short getStart() { + private short duration; + private boolean monday; + private boolean tuesday; + private boolean wednesday; + private boolean thursday; + private boolean friday; + private boolean saturday; + private boolean sunday; + private Integer workAreaId; + + public short getStart() { return start; } - public Short getDuration() { + public short getDuration() { return duration; } - public Boolean getMonday() { + public boolean getMonday() { return monday; } - public Boolean getTuesday() { + public boolean getTuesday() { return tuesday; } - public Boolean getWednesday() { + public boolean getWednesday() { return wednesday; } - public Boolean getThursday() { + public boolean getThursday() { return thursday; } - public Boolean getFriday() { + public boolean getFriday() { return friday; } - public Boolean getSaturday() { + public boolean getSaturday() { return saturday; } - public Boolean getSunday() { + public boolean getSunday() { return sunday; } - public int getWorkAreaId() { + public Integer getWorkAreaId() { return workAreaId; } - public void setStart(Short start) { + public void setStart(short start) { this.start = start; } - public void setDuration(Short duration) { + public void setDuration(short duration) { this.duration = duration; } - public void setMonday(Boolean monday) { + public void setMonday(boolean monday) { this.monday = monday; } - public void setTuesday(Boolean tuesday) { + public void setTuesday(boolean tuesday) { this.tuesday = tuesday; } - public void setWednesday(Boolean wednesday) { + public void setWednesday(boolean wednesday) { this.wednesday = wednesday; } - public void setThursday(Boolean thursday) { + public void setThursday(boolean thursday) { this.thursday = thursday; } - public void setFriday(Boolean friday) { + public void setFriday(boolean friday) { this.friday = friday; } - public void setSaturday(Boolean saturday) { + public void setSaturday(boolean saturday) { this.saturday = saturday; } - public void setSunday(Boolean sunday) { + public void setSunday(boolean sunday) { this.sunday = sunday; } - public void setWorkAreaId(int workAreaId) { + public void setWorkAreaId(Integer workAreaId) { this.workAreaId = workAreaId; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 719bc89656153..708dfcebf18e9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -92,8 +92,6 @@ public class AutomowerHandler extends BaseThingHandler { private @Nullable Mower mowerState; - // private Gson gson = new Gson(); - private Runnable automowerPollingRunnable = () -> { Bridge bridge = getBridge(); if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) { @@ -119,6 +117,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { * logger.debug("Sending calendar '{}'", command); * sendAutomowerCalendar(command.toString()); */ + } else if (CHANNEL_CALENDARTASKS.contains(channelUID.getId())) { + sendAutomowerCalendarTask(command, channelUID.getId()); } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -298,21 +298,63 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) * as provided when reading the channel */ - public void sendAutomowerCalendar(String calendar) { - logger.debug("Sending calendar '{}'", calendar); - String id = automowerId.get(); - try { - AutomowerBridge automowerBridge = getAutomowerBridge(); - if (automowerBridge != null) { - automowerBridge.sendAutomowerCalendar(id, calendar); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); + public void sendAutomowerCalendarTask(Command command, String channelID) { + String[] channelIDSplit = channelID.split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("calendartasks".length())) - 1; + String param = channelIDSplit[1]; + logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, + command.toString()); + + if (mowerState != null) { + CalendarTask calendarTask = mowerState.getAttributes().getCalendar().getTasks().get(index); + + if (command instanceof DecimalType cmd) { + if ("start".equals(param)) { + calendarTask.setStart((short) cmd.intValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration((short) cmd.intValue()); + } + } else if (command instanceof QuantityType cmd) { + if ("start".equals(param)) { + calendarTask.setStart(cmd.toUnit("min").shortValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration(cmd.toUnit("min").shortValue()); + } + } else if (command instanceof OnOffType cmd) { + boolean day = ((cmd == OnOffType.ON) ? true : false); + + if ("monday".equals(param)) { + calendarTask.setMonday(day); + } else if ("tuesday".equals(param)) { + calendarTask.setTuesday(day); + } else if ("wednesday".equals(param)) { + calendarTask.setWednesday(day); + } else if ("thursday".equals(param)) { + calendarTask.setThursday(day); + } else if ("friday".equals(param)) { + calendarTask.setFriday(day); + } else if ("saturday".equals(param)) { + calendarTask.setSaturday(day); + } else if ("sunday".equals(param)) { + calendarTask.setSunday(day); + } } - } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); - } - updateAutomowerState(); + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendarTask(id, calendarTask.getWorkAreaId(), calendarTask); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } } private String restrictedState(RestrictedReason reason) { @@ -334,29 +376,6 @@ private void updateChannelState(@Nullable Mower mower) { new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); } - /* - * if (mower.getAttributes().getMower().getState() != State.RESTRICTED) { - * updateState(CHANNEL_STATUS_STATE, new StringType(mower.getAttributes().getMower().getState().name())); - * updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(RestrictedReason.NONE.name())); - * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); - * } else { - * updateState(CHANNEL_STATUS_STATE, - * new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); - * updateState(CHANNEL_PLANNER_RESTRICTED_REASON, - * new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); - * if (mower.getAttributes().getPlanner().getRestrictedReason() != RestrictedReason.EXTERNAL) { - * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(0)); - * } else { - * updateState(CHANNEL_PLANNER_EXTERNAL_REASON, - * new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); - * } - * } - */ - /* - * logger.warn("CHANNEL_STATUS_LAST_UPDATE: {}, zoneID(): {}", - * mower.getAttributes().getMetadata().getStatusTimestamp(), - * ZoneId.of("UTC").toString()); - */ updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType( toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp(), ZoneId.of("UTC")))); updateState(CHANNEL_STATUS_BATTERY, @@ -380,10 +399,6 @@ private void updateChannelState(@Nullable Mower mower) { if (nextStartTimestamp == 0L) { updateState(CHANNEL_PLANNER_NEXT_START, UnDefType.NULL); } else { - /* - * logger.warn("CHANNEL_PLANNER_NEXT_START: {}, zoneID(): {}", nextStartTimestamp, - * timeZoneProvider.getTimeZone().toString()); - */ updateState(CHANNEL_PLANNER_NEXT_START, new DateTimeType(toZonedDateTime(nextStartTimestamp, mowerZoneId))); } From b120aeccab2c76418ef9f02f87f0578e2d70cf73 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 21:40:44 +0200 Subject: [PATCH 12/35] implemented sendAutomowerSettings Signed-off-by: Michael Weger --- .../internal/bridge/AutomowerBridge.java | 40 +++++++---- .../automowerconnect/AutomowerConnectApi.java | 17 ++++- .../automowerconnect/dto/CalendarTask.java | 6 +- .../api/automowerconnect/dto/WorkArea.java | 6 +- .../internal/things/AutomowerHandler.java | 68 ++++++++++++++++--- 5 files changed, 109 insertions(+), 28 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 33d1a50c74dc9..8a04e835ccaf4 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -30,6 +30,9 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettings; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.things.AutomowerCommand; import org.openhab.core.auth.client.oauth2.AccessTokenResponse; @@ -118,23 +121,15 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma } /** - * Sends a calendar to the automower + * Sends a calendarTask to the automower * * @param id The id of the mower - * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) + * @param workAreaId The Id of the work area this calendar belongs to (or null, if there is no work area support) + * @param calendarTask The calendar that should be sent. It is using the same json structure (start, duration, ...) * as provided when reading the channel * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - - /** - * Sends a calendar to the automower - * - * @param id The id of the mower - * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) - * as provided when reading the channel - * @throws AutomowerCommunicationException In case the query cannot be executed successfully - */ - public void sendAutomowerCalendarTask(String id, Integer workAreaId, CalendarTask calendarTask) + public void sendAutomowerCalendarTask(String id, Long workAreaId, CalendarTask calendarTask) throws AutomowerCommunicationException { List tasks = new ArrayList<>(); tasks.add(calendarTask); @@ -152,4 +147,25 @@ public void sendAutomowerCalendarTask(String id, Integer workAreaId, CalendarTas automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, workAreaId, request); } + + /** + * Sends Settings to the automower + * + * @param id The id of the mower + * @param settings The Settings that should be sent. It is using the same json structure + * as provided when reading the channel + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerSettings(String id, Settings settings) throws AutomowerCommunicationException { + MowerSettings mowerSettings = new MowerSettings(); + mowerSettings.setType("settings"); + mowerSettings.setAttributes(settings); + + MowerSettingsRequest request = new MowerSettingsRequest(); + request.setData(mowerSettings); + + logger.debug("request '{}'", gson.toJson(request)); + + automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, request); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index e00d27a5cd0f8..106c41a510270 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -28,6 +28,7 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerCommandRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; import org.slf4j.Logger; @@ -82,7 +83,7 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ checkForError(response, response.getStatus()); } - public void sendCalendar(String appKey, String token, String id, Integer workAreaId, MowerCalendardRequest calendar) + public void sendCalendar(String appKey, String token, String id, Long workAreaId, MowerCalendardRequest calendar) throws AutomowerCommunicationException { String url; if (workAreaId == null) { @@ -100,6 +101,20 @@ public void sendCalendar(String appKey, String token, String id, Integer workAre checkForError(response, response.getStatus()); } + public void sendSettings(String appKey, String token, String id, MowerSettingsRequest settings) + throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/settings"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + request.content(new StringContentProvider(gson.toJson(settings))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java index 8e42d589c6281..ad548a60ca345 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/CalendarTask.java @@ -32,7 +32,7 @@ public class CalendarTask { private boolean friday; private boolean saturday; private boolean sunday; - private Integer workAreaId; + private Long workAreaId; public short getStart() { return start; @@ -70,7 +70,7 @@ public boolean getSunday() { return sunday; } - public Integer getWorkAreaId() { + public Long getWorkAreaId() { return workAreaId; } @@ -110,7 +110,7 @@ public void setSunday(boolean sunday) { this.sunday = sunday; } - public void setWorkAreaId(Integer workAreaId) { + public void setWorkAreaId(Long workAreaId) { this.workAreaId = workAreaId; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java index a87f4c2a2415e..624e99dbbe738 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java @@ -19,7 +19,7 @@ public class WorkArea { private long workAreaId; private String name; private byte cuttingHeight; - private Boolean enabled; + private boolean enabled; private Byte progress; // Only available for EPOS mowers and systematic mowing work areas. private Long lastTimeCompleted; // Only available for EPOS mowers and systematic mowing work areas. @@ -47,11 +47,11 @@ public void setCuttingHeight(byte cuttingHeight) { this.cuttingHeight = cuttingHeight; } - public Boolean isEnabled() { + public boolean isEnabled() { return enabled; } - public void setEnabled(Boolean enabled) { + public void setEnabled(boolean enabled) { this.enabled = enabled; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 708dfcebf18e9..0932e5d21c620 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -34,9 +34,12 @@ import org.openhab.binding.automower.internal.bridge.AutomowerBridge; import org.openhab.binding.automower.internal.bridge.AutomowerBridgeHandler; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Headlight; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.State; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.StayOutZone; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.WorkArea; @@ -112,13 +115,11 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (RefreshType.REFRESH == command) { logger.debug("Refreshing channel '{}'", channelUID); refreshChannels(channelUID); - /* - * } else if (CHANNEL_CALENDAR_TASKS.equals(channelUID.getId())) { - * logger.debug("Sending calendar '{}'", command); - * sendAutomowerCalendar(command.toString()); - */ } else if (CHANNEL_CALENDARTASKS.contains(channelUID.getId())) { sendAutomowerCalendarTask(command, channelUID.getId()); + } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT) + || channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { + sendAutomowerSettings(command, channelUID.getId()); } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -293,10 +294,10 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM } /** - * Sends a calendar to the automower + * Sends a CalendarTask to the automower * - * @param calendar The calendar that should be sent. It is using the same json structure (start, duration, ...) - * as provided when reading the channel + * @param command The command that should be sent. E.g. a duration in min for the Start channel + * @param channelID The triggering channel */ public void sendAutomowerCalendarTask(Command command, String channelID) { String[] channelIDSplit = channelID.split("-"); @@ -357,6 +358,51 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { } } + /** + * Sends Settings to the automower + * + * @param command The command that should be sent. E.g. a number for the cuttingHeight channel + * @param channelID The triggering channel + */ + public void sendAutomowerSettings(Command command, String channelID) { + String[] channelIDSplit = channelID.split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("calendartasks".length())) - 1; + String param = channelIDSplit[1]; + logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, + command.toString()); + + if (mowerState != null) { + Settings settings = mowerState.getAttributes().getSettings(); + + if (command instanceof DecimalType cmd) { + if (CHANNEL_SETTING_HEADLIGHT_MODE.equals(channelID)) { + settings.setCuttingHeight((byte) cmd.intValue()); + } + } else if (command instanceof StringType cmd) { + if (CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME.equals(channelID)) { + Headlight headlight = new Headlight(); + headlight.setHeadlightMode(HeadlightMode.valueOf(cmd.toString())); + settings.setHeadlight(headlight); + } + } + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerSettings(id, settings); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } @@ -488,7 +534,11 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getFriday() == true)); updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSaturday() == true)); updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); + if (calendarTasks.get(i).getWorkAreaId() == null) { + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); + } } // clear remaining channels for (; j < CHANNEL_CALENDARTASKS.size(); j++) { From b2f789a2b134ba2c7768777fb4367e745471ca15 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 21:48:43 +0200 Subject: [PATCH 13/35] sendAutomowerSettings Signed-off-by: Michael Weger --- .../src/main/resources/OH-INF/thing/thing-types.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index e314534e6d91e..e674a12ac8b82 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -533,14 +533,12 @@ Number Prescaled cutting height, Range: 1-9 - String Information about headlights - From cdbfce724a65fade83ff79ca93eabccb6c364f8c Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 21:52:06 +0200 Subject: [PATCH 14/35] sendAutomowerSettings() Signed-off-by: Michael Weger --- .../automowerconnect/dto/MowerSettings.java | 37 ++ .../dto/MowerSettingsRequest.java | 28 ++ .../rest/api/automowerconnect/export.json.txt | 422 ++++++++++++++++++ 3 files changed, 487 insertions(+) create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java new file mode 100644 index 0000000000000..7025db2b6b372 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class MowerSettings { + private String type; + private Settings attributes; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Settings getAttributes() { + return attributes; + } + + public void setAttributes(Settings attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java new file mode 100644 index 0000000000000..d05673073b673 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author Markus Pfleger - Initial contribution + */ +public class MowerSettingsRequest { + private MowerSettings data; + + public MowerSettings getData() { + return data; + } + + public void setData(MowerSettings data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt new file mode 100644 index 0000000000000..4b01c69933359 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt @@ -0,0 +1,422 @@ +{ + "data":{ + "type":"mower", + "id":"feed-beef", + "attributes":{ + "system":{ + "name":"AM430X NERA", + "model":"Husqvarna Automower® 430X NERA", + "serialNumber":1234567 + }, + "battery":{ + "batteryPercent":83 + }, + "capabilities":{ + "headlights":true, + "workAreas":true, + "position":true, + "canConfirmError":true, + "stayOutZones":true + }, + "mower":{ + "mode":"MAIN_AREA", + - MAIN_AREA + - SECONDARY_AREA + - HOME + - DEMO + - UNKNOWN + - POI + "activity":"NOT_APPLICABLE", + - UNKNOWN + - NOT_APPLICABLE + - MOWING + - GOING_HOME + - CHARGING + - LEAVING + - PARKED_IN_CS + - STOPPED_IN_GARDEN + "inactiveReason":"NONE", + - 'NONE' + - 'PLANNING' + - 'SEARCHING_FOR_SATELLITES' + "state":"STOPPED", + "workAreaId":4294967295, + "errorCode":0, + "errorCodeTimestamp":0, + "isErrorConfirmable":false + }, + "calendar":{ + "tasks":[ + { + "start":420, + "duration":360, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":0 + }, + { + "start":0, + "duration":1440, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":20988 + }, + { + "start":0, + "duration":1440, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":2343 + }, + { + "start":0, + "duration":1440, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":20463 + }, + { + "start":0, + "duration":1440, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":26716 + }, + { + "start":450, + "duration":330, + "monday":true, + "tuesday":true, + "wednesday":true, + "thursday":true, + "friday":true, + "saturday":true, + "sunday":true, + "workAreaId":17746 + } + ] + }, + "planner":{ + "nextStartTimestamp":1728804600000, + "override":{ + "action":"NOT_ACTIVE" + - NOT_ACTIVE + - FORCE_PARK + - FORCE_MOW + }, + "restrictedReason":"WEEK_SCHEDULE" + - NONE + - WEEK_SCHEDULE + - PARK_OVERRIDE + - SENSOR + - DAILY_LIMIT + - FOTA + - FROST + - ALL_WORK_AREAS_COMPLETED + - EXTERNAL + }, + "metadata":{ + "connected":false, + "statusTimestamp":1728733281198 + }, + "workAreas":[ + { + "workAreaId":0, + "name":"", + "cuttingHeight":100, + "enabled":false + }, + { + "workAreaId":18569, + "name":"Kompost", + "cuttingHeight":100, + "enabled":false + }, + { + "workAreaId":17746, + "name":"Arbeitsbereich HERBST", + "cuttingHeight":88, + "enabled":true + }, + { + "workAreaId":20988, + "name":"Einfahrt", + "cuttingHeight":100, + "enabled":false + }, + { + "workAreaId":2343, + "name":"Garten N", + "cuttingHeight":100, + "enabled":false + }, + { + "workAreaId":20463, + "name":"Garten S", + "cuttingHeight":100, + "enabled":false + }, + { + "workAreaId":26716, + "name":"Fleckl", + "cuttingHeight":100, + "enabled":false + } + ], + "positions":[ + { + "latitude":49.4885543, + "longitude":11.4403704 + }, + { + "latitude":49.4886409, + "longitude":11.4402991 + }, + { + "latitude":49.4886345, + "longitude":11.4403477 + }, + { + "latitude":49.4886191, + "longitude":11.4404409 + }, + { + "latitude":49.4886154, + "longitude":11.4404571 + }, + { + "latitude":49.4885625, + "longitude":11.4404572 + }, + { + "latitude":49.4885106, + "longitude":11.44044 + }, + { + "latitude":49.4885359, + "longitude":11.440381 + }, + { + "latitude":49.4886038, + "longitude":11.4403824 + }, + { + "latitude":49.4885955, + "longitude":11.4403705 + }, + { + "latitude":49.4885553, + "longitude":11.4403891 + }, + { + "latitude":49.4885065, + "longitude":11.4404362 + }, + { + "latitude":49.4885752, + "longitude":11.4404618 + }, + { + "latitude":49.488634, + "longitude":11.4404194 + }, + { + "latitude":49.4886403, + "longitude":11.4403184 + }, + { + "latitude":49.4886432, + "longitude":11.4403063 + }, + { + "latitude":49.4886393, + "longitude":11.4403557 + }, + { + "latitude":49.4886167, + "longitude":11.4404489 + }, + { + "latitude":49.4885441, + "longitude":11.4404501 + }, + { + "latitude":49.4885495, + "longitude":11.4404615 + }, + { + "latitude":49.4886104, + "longitude":11.4404536 + }, + { + "latitude":49.488601, + "longitude":11.4404616 + }, + { + "latitude":49.4885229, + "longitude":11.4404567 + }, + { + "latitude":49.4885221, + "longitude":11.4404052 + }, + { + "latitude":49.4885585, + "longitude":11.4403508 + }, + { + "latitude":49.4885367, + "longitude":11.4402899 + }, + { + "latitude":49.4886131, + "longitude":11.4403878 + }, + { + "latitude":49.4885686, + "longitude":11.4404124 + }, + { + "latitude":49.4885122, + "longitude":11.4403419 + }, + { + "latitude":49.4885847, + "longitude":11.4403082 + }, + { + "latitude":49.4885781, + "longitude":11.440312 + }, + { + "latitude":49.488555, + "longitude":11.4403824 + }, + { + "latitude":49.488523, + "longitude":11.4403905 + }, + { + "latitude":49.488579, + "longitude":11.4403191 + }, + { + "latitude":49.4886067, + "longitude":11.4403727 + }, + { + "latitude":49.4885113, + "longitude":11.4403436 + }, + { + "latitude":49.4885584, + "longitude":11.4403292 + }, + { + "latitude":49.488593, + "longitude":11.4404123 + }, + { + "latitude":49.488536, + "longitude":11.4403742 + }, + { + "latitude":49.4885764, + "longitude":11.4403652 + }, + { + "latitude":49.4885773, + "longitude":11.4404133 + }, + { + "latitude":49.4885379, + "longitude":11.4402983 + }, + { + "latitude":49.4885529, + "longitude":11.4404118 + }, + { + "latitude":49.488566, + "longitude":11.4402938 + }, + { + "latitude":49.488538, + "longitude":11.4403783 + }, + { + "latitude":49.4886072, + "longitude":11.4403713 + }, + { + "latitude":49.4885509, + "longitude":11.4403495 + }, + { + "latitude":49.488525, + "longitude":11.4403647 + }, + { + "latitude":49.4885137, + "longitude":11.4403337 + }, + { + "latitude":49.488588, + "longitude":11.4403226 + } + ], + "settings":{ + "cuttingHeight":8, + "headlight":{ + "mode":"EVENING_AND_NIGHT" + } + }, + "statistics":{ + "cuttingBladeUsageTime":830796, + "numberOfChargingCycles":98, + "numberOfCollisions":6940, + "totalChargingTime":224782, + "totalCuttingTime":830796, + "totalDriveDistance":369223, + "totalRunningTime":900545, + "totalSearchingTime":51789 + }, + "stayOutZones":{ + "zones":[ + { + "id":"ad18bfae-33ae-45f2-9dc5-0b7c1b4b4fd2", + "name":"Ausgeschlossener Bereich", + "enabled":true + } + ], + "dirty":false + } + } + } + } + From 7a292c85d3a7d83b8d208ad04b25058042bcc35c Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 13 Oct 2024 21:55:46 +0200 Subject: [PATCH 15/35] re-trigger build Signed-off-by: Michael Weger From 0f62224d94a2ddeb9611fe357c44a266eae38aa9 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Thu, 17 Oct 2024 19:40:10 +0200 Subject: [PATCH 16/35] added CHANNEL_STATUS_WORK_AREA_ID channels Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 2 + .../internal/bridge/AutomowerBridge.java | 4 +- .../automowerconnect/AutomowerConnectApi.java | 10 +- .../automowerconnect/dto/Capabilities.java | 20 +- .../internal/things/AutomowerHandler.java | 208 ++++++++++-------- .../resources/OH-INF/thing/thing-types.xml | 16 ++ 6 files changed, 153 insertions(+), 107 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 93952bc9641fb..15ea36b41098a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -43,6 +43,8 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATUS_INACTIVE_REASON = GROUP_STATUS + "inactive-reason"; public static final String CHANNEL_STATUS_STATE = GROUP_STATUS + "state"; public static final String CHANNEL_STATUS_LAST_UPDATE = GROUP_STATUS + "last-update"; + public static final String CHANNEL_STATUS_WORK_AREA_ID = GROUP_STATUS + "work-area-id"; + public static final String CHANNEL_STATUS_WORK_AREA = GROUP_STATUS + "work-area"; public static final String CHANNEL_STATUS_BATTERY = GROUP_STATUS + "battery"; public static final String CHANNEL_STATUS_ERROR_CODE = GROUP_STATUS + "error-code"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 8a04e835ccaf4..72344b693c220 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -129,7 +129,7 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma * as provided when reading the channel * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerCalendarTask(String id, Long workAreaId, CalendarTask calendarTask) + public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long workAreaId, CalendarTask calendarTask) throws AutomowerCommunicationException { List tasks = new ArrayList<>(); tasks.add(calendarTask); @@ -145,7 +145,7 @@ public void sendAutomowerCalendarTask(String id, Long workAreaId, CalendarTask c logger.debug("request '{}'", gson.toJson(request)); - automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, workAreaId, request); + automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, hasWorkAreas, workAreaId, request); } /** diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 106c41a510270..6e8f92d53fd34 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -83,13 +83,13 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ checkForError(response, response.getStatus()); } - public void sendCalendar(String appKey, String token, String id, Long workAreaId, MowerCalendardRequest calendar) - throws AutomowerCommunicationException { + public void sendCalendar(String appKey, String token, String id, boolean hasWorkAreas, Long workAreaId, + MowerCalendardRequest calendar) throws AutomowerCommunicationException { String url; - if (workAreaId == null) { - url = getBaseUrl() + "/mowers/" + id + "/calendar"; - } else { + if (hasWorkAreas) { url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId + "/calendar"; + } else { + url = getBaseUrl() + "/mowers/" + id + "/calendar"; } final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.POST); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java index 8a7c4efe841e5..58b5f4cedc87c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java @@ -16,29 +16,29 @@ * @author Markus Pfleger - Initial contribution */ public class Capabilities { - private Boolean canConfirmError; - private Boolean headlights; - private Boolean position; - private Boolean stayOutZones; - private Boolean workAreas; + private boolean canConfirmError; + private boolean headlights; + private boolean position; + private boolean stayOutZones; + private boolean workAreas; - public Boolean canConfirmError() { + public boolean canConfirmError() { return canConfirmError; } - public Boolean hasHeadlights() { + public boolean hasHeadlights() { return headlights; } - public Boolean hasPosition() { + public boolean hasPosition() { return position; } - public Boolean hasStayOutZones() { + public boolean hasStayOutZones() { return stayOutZones; } - public Boolean hasWorkAreas() { + public boolean hasWorkAreas() { return workAreas; } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 0932e5d21c620..b5640eca19285 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -215,8 +215,14 @@ private void stopAutomowerPolling() { } private boolean isValidResult(@Nullable Mower mower) { - return mower != null && mower.getAttributes() != null && mower.getAttributes().getMetadata() != null - && mower.getAttributes().getBattery() != null && mower.getAttributes().getSystem() != null; + return ((mower != null && mower.getAttributes() != null) && (mower.getAttributes().getMetadata() != null) + && (mower.getAttributes().getBattery() != null) && (mower.getAttributes().getSystem() != null) + && (mower.getAttributes().getCalendar() != null) + && (mower.getAttributes().getCalendar().getTasks() != null) + && (mower.getAttributes().getCapabilities() != null) && (mower.getAttributes().getMower() != null) + && (mower.getAttributes().getPlanner() != null) && (mower.getAttributes().getPositions() != null) + && (mower.getAttributes().getSettings() != null) && (mower.getAttributes().getStatistics() != null) + && (mower.getAttributes().getSystem() != null)); } private boolean isConnected(@Nullable Mower mower) { @@ -289,7 +295,6 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM } catch (AutomowerCommunicationException e) { logger.warn("Unable to send command to automower: {}, Error: {}", id, e.getMessage()); } - updateAutomowerState(); } @@ -305,57 +310,62 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { String param = channelIDSplit[1]; logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, command.toString()); - - if (mowerState != null) { + if (isValidResult(mowerState)) { CalendarTask calendarTask = mowerState.getAttributes().getCalendar().getTasks().get(index); - - if (command instanceof DecimalType cmd) { - if ("start".equals(param)) { - calendarTask.setStart((short) cmd.intValue()); - } else if ("duration".equals(param)) { - calendarTask.setDuration((short) cmd.intValue()); - } - } else if (command instanceof QuantityType cmd) { - if ("start".equals(param)) { - calendarTask.setStart(cmd.toUnit("min").shortValue()); - } else if ("duration".equals(param)) { - calendarTask.setDuration(cmd.toUnit("min").shortValue()); - } - } else if (command instanceof OnOffType cmd) { - boolean day = ((cmd == OnOffType.ON) ? true : false); - - if ("monday".equals(param)) { - calendarTask.setMonday(day); - } else if ("tuesday".equals(param)) { - calendarTask.setTuesday(day); - } else if ("wednesday".equals(param)) { - calendarTask.setWednesday(day); - } else if ("thursday".equals(param)) { - calendarTask.setThursday(day); - } else if ("friday".equals(param)) { - calendarTask.setFriday(day); - } else if ("saturday".equals(param)) { - calendarTask.setSaturday(day); - } else if ("sunday".equals(param)) { - calendarTask.setSunday(day); + if (calendarTask != null) { + if (command instanceof DecimalType cmd) { + if ("start".equals(param)) { + calendarTask.setStart((short) cmd.intValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration((short) cmd.intValue()); + } + } else if (command instanceof QuantityType cmd) { + cmd = cmd.toUnit("min"); + if (cmd != null) { + if ("start".equals(param)) { + calendarTask.setStart(cmd.shortValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration(cmd.shortValue()); + } + } + } else if (command instanceof OnOffType cmd) { + boolean day = ((cmd == OnOffType.ON) ? true : false); + + if ("monday".equals(param)) { + calendarTask.setMonday(day); + } else if ("tuesday".equals(param)) { + calendarTask.setTuesday(day); + } else if ("wednesday".equals(param)) { + calendarTask.setWednesday(day); + } else if ("thursday".equals(param)) { + calendarTask.setThursday(day); + } else if ("friday".equals(param)) { + calendarTask.setFriday(day); + } else if ("saturday".equals(param)) { + calendarTask.setSaturday(day); + } else if ("sunday".equals(param)) { + calendarTask.setSunday(day); + } } - } - String id = automowerId.get(); - try { - AutomowerBridge automowerBridge = getAutomowerBridge(); - if (automowerBridge != null) { - automowerBridge.sendAutomowerCalendarTask(id, calendarTask.getWorkAreaId(), calendarTask); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/conf-error-no-bridge"); + String id = automowerId.get(); + + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendarTask(id, + mowerState.getAttributes().getCapabilities().hasWorkAreas(), + calendarTask.getWorkAreaId(), calendarTask); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); } - } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); } - - updateAutomowerState(); } + updateAutomowerState(); } /** @@ -371,7 +381,7 @@ public void sendAutomowerSettings(Command command, String channelID) { logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, command.toString()); - if (mowerState != null) { + if (isValidResult(mowerState)) { Settings settings = mowerState.getAttributes().getSettings(); if (command instanceof DecimalType cmd) { @@ -380,9 +390,13 @@ public void sendAutomowerSettings(Command command, String channelID) { } } else if (command instanceof StringType cmd) { if (CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME.equals(channelID)) { - Headlight headlight = new Headlight(); - headlight.setHeadlightMode(HeadlightMode.valueOf(cmd.toString())); - settings.setHeadlight(headlight); + try { + Headlight headlight = new Headlight(); + headlight.setHeadlightMode(HeadlightMode.valueOf(cmd.toString())); + settings.setHeadlight(headlight); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", cmd.toString(), e.getMessage()); + } } } @@ -407,9 +421,20 @@ private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } + private @Nullable String workAreaName(Mower mower, long workAreaId) { + List workAreas = mower.getAttributes().getWorkAreas(); + for (int i = 0; i < workAreas.size(); i++) { + if (workAreas.get(i).getWorkAreaId() == workAreaId) { + return workAreas.get(i).getName(); + } + } + return null; + } + private void updateChannelState(@Nullable Mower mower) { if (isValidResult(mower)) { updateState(CHANNEL_STATUS_NAME, new StringType(mower.getAttributes().getSystem().getName())); + updateState(CHANNEL_STATUS_MODE, new StringType(mower.getAttributes().getMower().getMode().name())); updateState(CHANNEL_STATUS_ACTIVITY, new StringType(mower.getAttributes().getMower().getActivity().name())); updateState(CHANNEL_STATUS_INACTIVE_REASON, @@ -422,6 +447,14 @@ private void updateChannelState(@Nullable Mower mower) { new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); } + updateState(CHANNEL_STATUS_WORK_AREA_ID, new DecimalType(mower.getAttributes().getMower().getWorkAreaId())); + if (workAreaName(mower, mower.getAttributes().getMower().getWorkAreaId()) == null) { + updateState(CHANNEL_STATUS_WORK_AREA, UnDefType.NULL); + } else { + updateState(CHANNEL_STATUS_WORK_AREA, + new StringType(workAreaName(mower, mower.getAttributes().getMower().getWorkAreaId()))); + } + updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType( toZonedDateTime(mower.getAttributes().getMetadata().getStatusTimestamp(), ZoneId.of("UTC")))); updateState(CHANNEL_STATUS_BATTERY, @@ -438,7 +471,7 @@ private void updateChannelState(@Nullable Mower mower) { } updateState(CHANNEL_STATUS_ERROR_CONFIRMABLE, - OnOffType.from(mower.getAttributes().getMower().getIsErrorConfirmable() == true)); + OnOffType.from(mower.getAttributes().getMower().getIsErrorConfirmable())); long nextStartTimestamp = mower.getAttributes().getPlanner().getNextStartTimestamp(); // If next start timestamp is 0 it means the mower should start now, so using current timestamp @@ -502,8 +535,7 @@ private void updateChannelState(@Nullable Mower mower) { new DecimalType(positions.get(i).getLongitude()))); } - updateState(CHANNEL_STAYOUTZONES_DIRTY, - OnOffType.from(mower.getAttributes().getStayOutZones().isDirty() == true)); + updateState(CHANNEL_STAYOUTZONES_DIRTY, OnOffType.from(mower.getAttributes().getStayOutZones().isDirty())); /* * updateState(CHANNEL_CALENDAR_TASKS, @@ -526,14 +558,13 @@ private void updateChannelState(@Nullable Mower mower) { new QuantityType<>(calendarTasks.get(i).getStart(), Units.MINUTE)); updateState(CHANNEL_CALENDARTASKS.get(j++), new QuantityType<>(calendarTasks.get(i).getDuration(), Units.MINUTE)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getMonday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getTuesday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), - OnOffType.from(calendarTasks.get(i).getWednesday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getThursday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getFriday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSaturday() == true)); - updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday() == true)); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getMonday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getTuesday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getWednesday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getThursday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getFriday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSaturday())); + updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday())); if (calendarTasks.get(i).getWorkAreaId() == null) { updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); } else { @@ -550,7 +581,7 @@ private void updateChannelState(@Nullable Mower mower) { for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); - updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled() == true)); + updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled())); } // clear remaining channels for (; j < CHANNEL_STAYOUTZONES.size(); j++) { @@ -567,7 +598,7 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workAreas.get(i).getName())); updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workAreas.get(i).getCuttingHeight(), Units.PERCENT)); - updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workAreas.get(i).isEnabled() == true)); + updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workAreas.get(i).isEnabled())); if (workAreas.get(i).getProgress() != null) { updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workAreas.get(i).getProgress(), Units.PERCENT)); @@ -593,31 +624,28 @@ private void updateChannelState(@Nullable Mower mower) { private void initializeProperties(@Nullable Mower mower) { Map properties = editProperties(); - properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); - - if (mower.getAttributes() != null) { - if (mower.getAttributes().getSystem() != null) { - properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, - mower.getAttributes().getSystem().getSerialNumber()); - properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); - properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); - } - if (mower.getAttributes().getCapabilities() != null) { - logger.warn("mower.getAttributes().getCapabilities(): {}, canConfirmError(): {}", - mower.getAttributes().getCapabilities(), - mower.getAttributes().getCapabilities().canConfirmError().toString()); - - properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, - mower.getAttributes().getCapabilities().canConfirmError().toString()); - properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, - mower.getAttributes().getCapabilities().hasHeadlights().toString()); - properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, - mower.getAttributes().getCapabilities().hasPosition().toString()); - properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, - mower.getAttributes().getCapabilities().hasStayOutZones().toString()); - properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, - mower.getAttributes().getCapabilities().hasWorkAreas().toString()); - } + if (isValidResult(mower)) { + properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); + + properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, + mower.getAttributes().getSystem().getSerialNumber()); + properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); + properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); + + logger.warn("mower.getAttributes().getCapabilities(): {}, canConfirmError(): {}", + mower.getAttributes().getCapabilities(), + (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); + + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, + (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, + (mower.getAttributes().getCapabilities().hasHeadlights() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, + (mower.getAttributes().getCapabilities().hasPosition() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, + (mower.getAttributes().getCapabilities().hasStayOutZones() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, + (mower.getAttributes().getCapabilities().hasWorkAreas() ? "yes" : "no")); } } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index e674a12ac8b82..35b924726d937 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -49,6 +49,8 @@ + + @@ -453,6 +455,20 @@ + + Number + + Id of the active work area + + + + + String + + Name of the active work area + + + DateTime From 7c9ad9d9ab2fc399929aa6008c1c83b48dbdf915 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Thu, 17 Oct 2024 20:26:18 +0200 Subject: [PATCH 17/35] implemented sendAutomowerConfirmError() Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 1 + .../internal/bridge/AutomowerBridge.java | 10 ++++++ .../automowerconnect/AutomowerConnectApi.java | 11 +++++++ .../internal/things/AutomowerHandler.java | 32 +++++++++++++++++++ .../resources/OH-INF/thing/thing-types.xml | 7 ++++ 5 files changed, 61 insertions(+) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 15ea36b41098a..1f264f0900342 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -221,6 +221,7 @@ public class AutomowerBindingConstants { public static final String CHANNEL_COMMAND_PARK = GROUP_COMMANDS + "park"; public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMANDS + "park_until_next_schedule"; public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMANDS + "park_until_further_notice"; + public static final String CHANNEL_COMMAND_CONFIRM_ERROR = GROUP_COMMANDS + "confirm_error"; // Automower properties public static final String AUTOMOWER_ID = "mowerId"; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 72344b693c220..69083e27554a9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -168,4 +168,14 @@ public void sendAutomowerSettings(String id, Settings settings) throws Automower automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, request); } + + /** + * Confirm current non fatal error on the mower + * + * @param id The id of the mower + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationException { + automowerApi.sendConfirmError(appKey, authenticate().getAccessToken(), id); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 6e8f92d53fd34..071916021ed00 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -115,6 +115,17 @@ public void sendSettings(String appKey, String token, String id, MowerSettingsRe checkForError(response, response.getStatus()); } + public void sendConfirmError(String appKey, String token, String id) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/errors/confirm"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index b5640eca19285..8af3e99e6041d 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -120,6 +120,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT) || channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { sendAutomowerSettings(command, channelUID.getId()); + } else if (channelUID.getId().equals(CHANNEL_COMMAND_CONFIRM_ERROR)) { + sendAutomowerConfirmError(command); } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -417,6 +419,36 @@ public void sendAutomowerSettings(Command command, String channelID) { } } + /** + * Confirm current non fatal error on the mower + * + * @param command The command that was used to trigger the channel + */ + public void sendAutomowerConfirmError(Command command) { + logger.debug("Sending ConfirmError '{}'", command.toString()); + if (command instanceof OnOffType cmd) { + if (cmd == OnOffType.ON) { + updateState(CHANNEL_COMMAND_CONFIRM_ERROR, OnOffType.OFF); + if (isValidResult(mowerState) && (mowerState.getAttributes().getCapabilities().canConfirmError()) + && (mowerState.getAttributes().getMower().getIsErrorConfirmable())) { + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerConfirmError(id); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send ConfirmError to automower: {}, Error: {}", id, e.getMessage()); + } + updateAutomowerState(); + } + } + } + } + private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 35b924726d937..f273f1acd72b1 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -191,6 +191,7 @@ + @@ -799,4 +800,10 @@ Park and pause the mower schedule until manual resume + + + Switch + + Confirm current non fatal error + From 570254919807e124319be8aa9c2eafd65a0e757c Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Thu, 17 Oct 2024 20:56:39 +0200 Subject: [PATCH 18/35] fixed updateProperties() Signed-off-by: Michael Weger --- .../internal/discovery/AutomowerDiscoveryService.java | 11 +++++++++++ .../automower/internal/things/AutomowerHandler.java | 9 +++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java index abf43c6884b30..eae36ab8519c5 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/discovery/AutomowerDiscoveryService.java @@ -63,6 +63,17 @@ protected void startScan() { properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); + properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, + (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, + (mower.getAttributes().getCapabilities().hasHeadlights() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, + (mower.getAttributes().getCapabilities().hasPosition() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, + (mower.getAttributes().getCapabilities().hasStayOutZones() ? "yes" : "no")); + properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, + (mower.getAttributes().getCapabilities().hasWorkAreas() ? "yes" : "no")); + DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(mowerThingUid) .withThingType(thingTypeUID).withProperties(properties).withBridge(bridgeUID) .withRepresentationProperty(AutomowerBindingConstants.AUTOMOWER_ID) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 8af3e99e6041d..c83e132dd30f1 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -654,9 +654,8 @@ private void updateChannelState(@Nullable Mower mower) { } private void initializeProperties(@Nullable Mower mower) { - Map properties = editProperties(); - if (isValidResult(mower)) { + Map properties = editProperties(); properties.put(AutomowerBindingConstants.AUTOMOWER_ID, mower.getId()); properties.put(AutomowerBindingConstants.AUTOMOWER_SERIAL_NUMBER, @@ -664,10 +663,6 @@ private void initializeProperties(@Nullable Mower mower) { properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); - logger.warn("mower.getAttributes().getCapabilities(): {}, canConfirmError(): {}", - mower.getAttributes().getCapabilities(), - (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); - properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, @@ -678,6 +673,8 @@ private void initializeProperties(@Nullable Mower mower) { (mower.getAttributes().getCapabilities().hasStayOutZones() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, (mower.getAttributes().getCapabilities().hasWorkAreas() ? "yes" : "no")); + + updateProperties(properties); } } From 887f5273da354aeac445bb16ef90578203e40faf Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Fri, 18 Oct 2024 21:16:59 +0200 Subject: [PATCH 19/35] added sendAutomowerStayOutZones added actions Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 56 +++--- .../internal/actions/AutomowerActions.java | 44 +++++ .../internal/bridge/AutomowerBridge.java | 25 +-- .../automowerconnect/AutomowerConnectApi.java | 20 ++- .../api/automowerconnect/dto/MowerApp.java | 6 +- .../dto/MowerStayOutZone.java | 46 +++++ .../dto/MowerStayOutZoneAttributes.java | 28 +++ .../dto/MowerStayOutZoneRequest.java | 28 +++ .../internal/things/AutomowerHandler.java | 161 ++++++++++++------ .../resources/OH-INF/thing/thing-types.xml | 16 +- 10 files changed, 331 insertions(+), 99 deletions(-) create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 1f264f0900342..b9381b3370a6d 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -49,30 +49,41 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATUS_ERROR_CODE = GROUP_STATUS + "error-code"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; public static final String CHANNEL_STATUS_ERROR_CONFIRMABLE = GROUP_STATUS + "error-confirmable"; - public static final String CHANNEL_PLANNER_NEXT_START = GROUP_STATUS + "planner-next-start"; - public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_STATUS + "planner-override-action"; - public static final String CHANNEL_PLANNER_RESTRICTED_REASON = GROUP_STATUS + "planner-restricted-reason"; - public static final String CHANNEL_PLANNER_EXTERNAL_REASON = GROUP_STATUS + "planner-external-reason"; - public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_STATUS + "setting-cutting-height"; - public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_STATUS + "setting-headlight-mode"; - public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATUS + + // List of all planner Channel ids + public static final String GROUP_PLANNER = ""; // no channel group in use at the moment, we'll possibly introduce + // this in a future release + public static final String CHANNEL_PLANNER_NEXT_START = GROUP_PLANNER + "planner-next-start"; + public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_PLANNER + "planner-override-action"; + public static final String CHANNEL_PLANNER_RESTRICTED_REASON = GROUP_PLANNER + "planner-restricted-reason"; + public static final String CHANNEL_PLANNER_EXTERNAL_REASON = GROUP_PLANNER + "planner-external-reason"; + + // List of all setting Channel ids + public static final String GROUP_SETTING = ""; // no channel group in use at the moment, we'll possibly introduce + // this in a future release + public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_SETTING + "setting-cutting-height"; + public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_SETTING + "setting-headlight-mode"; + + // List of all setting Channel ids + public static final String GROUP_STATISTIC = ""; // no channel group in use at the moment, we'll possibly introduce + // this in a future release + public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATISTIC + "stat-cutting-blade-usage-time"; - public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATUS + public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATISTIC + "stat-number-of-charging-cycles"; - public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATUS + "stat-number-of-collisions"; - public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATUS + "stat-total-charging-time"; - public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATUS + "stat-total-cutting-time"; - public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATUS + "stat-total-cutting-percent"; - public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATUS + "stat-total-drive-distance"; - public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATUS + "stat-total-running-time"; - public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATUS + "stat-total-searching-time"; - public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATUS + public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATISTIC + "stat-number-of-collisions"; + public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATISTIC + "stat-total-charging-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATISTIC + "stat-total-cutting-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATISTIC + "stat-total-cutting-percent"; + public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATISTIC + "stat-total-drive-distance"; + public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATISTIC + "stat-total-running-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATISTIC + "stat-total-searching-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATISTIC + "stat-total-searching-percent"; // Calendar Task Channels ids public static final String GROUP_CALENDARTASKS = ""; // no channel group in use at the moment, we'll possibly - // introduce - // this in a future release + // introduce this in a future release public static final ArrayList CHANNEL_CALENDARTASKS = new ArrayList<>(List.of( GROUP_CALENDARTASKS + "calendartasks01-start", GROUP_CALENDARTASKS + "calendartasks01-duration", GROUP_CALENDARTASKS + "calendartasks01-monday", GROUP_CALENDARTASKS + "calendartasks01-tuesday", @@ -127,8 +138,7 @@ public class AutomowerBindingConstants { // Position Channels ids public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly - // introduce - // this in a future release + // introduce this in a future release public static final String LAST_POSITION = GROUP_POSITIONS + "last-position"; public static final ArrayList CHANNEL_POSITIONS = new ArrayList<>( List.of(GROUP_POSITIONS + "position01", GROUP_POSITIONS + "position02", GROUP_POSITIONS + "position03", @@ -151,8 +161,7 @@ public class AutomowerBindingConstants { // Stayout Zones Channels ids public static final String GROUP_STAYOUTZONES = ""; // no channel group in use at the moment, we'll possibly - // introduce - // this in a future release + // introduce this in a future release public static final String CHANNEL_STAYOUTZONES_DIRTY = GROUP_STAYOUTZONES + "dirty"; public static final ArrayList CHANNEL_STAYOUTZONES = new ArrayList<>(List.of( GROUP_STAYOUTZONES + "zone01-id", GROUP_STAYOUTZONES + "zone01-name", GROUP_STAYOUTZONES + "zone01-enabled", @@ -169,8 +178,7 @@ public class AutomowerBindingConstants { // Work Areas Channels ids public static final String GROUP_WORKAREAS = ""; // no channel group in use at the moment, we'll possibly - // introduce - // this in a future release + // introduce this in a future release public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>( List.of(GROUP_WORKAREAS + "workareas01-id", GROUP_WORKAREAS + "workareas01-name", GROUP_WORKAREAS + "workareas01-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index b8626e878131c..fa509e99dd077 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -131,4 +131,48 @@ public void resumeSchedule() { public static void resumeSchedule(ThingActions actions) { ((AutomowerActions) actions).resumeSchedule(); } + + @RuleAction(label = "@text/action-confirm-error-label", description = "@text/action-confirm-error-desc") + public void confirmError() { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerConfirmError(); + } + } + + public static void confirmError(ThingActions actions) { + ((AutomowerActions) actions).confirmError(); + } + + @RuleAction(label = "@text/action-set-cutting-height-label", description = "@text/action-set-cutting-height-desc") + public void setCuttingHeight( + @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerSettingCuttingHeight(cuttingHeight); + } + } + + public static void setCuttingHeight(ThingActions actions, byte cuttingHeight) { + ((AutomowerActions) actions).setCuttingHeight(cuttingHeight); + } + + @RuleAction(label = "@text/action-set-headlight-mode-label", description = "@text/action-set-headlight-mode-desc") + public void setHeadlightMode( + @ActionInput(name = "headlight-mode", label = "@text/action-input-headlight-mode-label", description = "@text/action-input-headlight-mode-desc") String headlightMode) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerSettingHeadlightMode(headlightMode); + } + } + + public static void setHeadlightMode(ThingActions actions, String headlightMode) { + ((AutomowerActions) actions).setHeadlightMode(headlightMode); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 69083e27554a9..1951cc14c3e85 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -32,6 +32,7 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettings; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.things.AutomowerCommand; @@ -39,10 +40,6 @@ import org.openhab.core.auth.client.oauth2.OAuthClientService; import org.openhab.core.auth.client.oauth2.OAuthException; import org.openhab.core.auth.client.oauth2.OAuthResponseException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; /** * The {@link AutomowerBridge} allows the communication to the various Husqvarna rest apis like the @@ -57,9 +54,6 @@ public class AutomowerBridge { private final AutomowerConnectApi automowerApi; - private final Logger logger = LoggerFactory.getLogger(AutomowerBridge.class); - private Gson gson = new Gson(); - public AutomowerBridge(OAuthClientService authService, String appKey, HttpClient httpClient, ScheduledExecutorService scheduler) { this.authService = authService; @@ -143,8 +137,6 @@ public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long work MowerCalendardRequest request = new MowerCalendardRequest(); request.setData(mowerCalendar); - logger.debug("request '{}'", gson.toJson(request)); - automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, hasWorkAreas, workAreaId, request); } @@ -164,8 +156,6 @@ public void sendAutomowerSettings(String id, Settings settings) throws Automower MowerSettingsRequest request = new MowerSettingsRequest(); request.setData(mowerSettings); - logger.debug("request '{}'", gson.toJson(request)); - automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, request); } @@ -178,4 +168,17 @@ public void sendAutomowerSettings(String id, Settings settings) throws Automower public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationException { automowerApi.sendConfirmError(appKey, authenticate().getAccessToken(), id); } + + /** + * Enable or disable stay out zone + * + * @param id The id of the mower + * @param zoneId The id of the stay out zone + * @param zoneRequest The new zone status + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerStayOutZones(String id, String zoneId, MowerStayOutZoneRequest zoneRequest) + throws AutomowerCommunicationException { + automowerApi.sendStayOutZones(appKey, authenticate().getAccessToken(), id, zoneId, zoneRequest); + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 071916021ed00..954ce5c1f9856 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -29,10 +29,9 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.gson.JsonSyntaxException; @@ -43,8 +42,6 @@ */ @NonNullByDefault public class AutomowerConnectApi extends HusqvarnaApi { - private final Logger logger = LoggerFactory.getLogger(AutomowerConnectApi.class); - public AutomowerConnectApi(HttpClient httpClient) { super(httpClient); } @@ -126,6 +123,20 @@ public void sendConfirmError(String appKey, String token, String id) throws Auto checkForError(response, response.getStatus()); } + public void sendStayOutZones(String appKey, String token, String id, String zoneId, + MowerStayOutZoneRequest zoneRequest) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/stayOutZones/" + zoneId; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.PATCH); + + request.content(new StringContentProvider(gson.toJson(zoneRequest))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); @@ -151,7 +162,6 @@ private T parseResponse(ContentResponse response, Class type) throws Auto int statusCode = response.getStatus(); checkForError(response, statusCode); - logger.warn("response: {}", response.getContentAsString()); try { return gson.fromJson(response.getContentAsString(), type); } catch (JsonSyntaxException e) { diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java index 66d122a51411c..19ad4695025b6 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerApp.java @@ -20,7 +20,7 @@ public class MowerApp { private Activity activity; private InactiveReason inactiveReason; private State state; - private long workAreaId; + private Long workAreaId; private int errorCode; private long errorCodeTimestamp; private Boolean isErrorConfirmable; @@ -57,11 +57,11 @@ public void setState(State state) { this.state = state; } - public long getWorkAreaId() { + public Long getWorkAreaId() { return workAreaId; } - public void setWorkAreaId(long workAreaId) { + public void setWorkAreaId(Long workAreaId) { this.workAreaId = workAreaId; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java new file mode 100644 index 0000000000000..405be3571f27d --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZone.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZone { + private String type; + private String id; + private MowerStayOutZoneAttributes attributes; + + public String getType() { + return type; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setType(String type) { + this.type = type; + } + + public MowerStayOutZoneAttributes getAttributes() { + return attributes; + } + + public void setAttributes(MowerStayOutZoneAttributes attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java new file mode 100644 index 0000000000000..712d39e1f363c --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneAttributes.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZoneAttributes { + private boolean enable; + + public boolean getEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java new file mode 100644 index 0000000000000..f9b3eca292281 --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerStayOutZoneRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerStayOutZoneRequest { + private MowerStayOutZone data; + + public MowerStayOutZone getData() { + return data; + } + + public void setData(MowerStayOutZone data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index c83e132dd30f1..28d093d518d94 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -37,6 +37,9 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Headlight; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZone; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; @@ -117,11 +120,29 @@ public void handleCommand(ChannelUID channelUID, Command command) { refreshChannels(channelUID); } else if (CHANNEL_CALENDARTASKS.contains(channelUID.getId())) { sendAutomowerCalendarTask(command, channelUID.getId()); - } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT) - || channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { - sendAutomowerSettings(command, channelUID.getId()); + } else if (CHANNEL_STAYOUTZONES.contains(channelUID.getId())) { + if (channelUID.getId().contains("-enabled")) { + if (command instanceof OnOffType cmd) { + String[] channelIDSplit = channelUID.getId().split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("zone".length())) - 1; + sendAutomowerStayOutZones(((cmd == OnOffType.ON) ? true : false), index); + } + } + } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT)) { + if (command instanceof DecimalType cmd) { + sendAutomowerSettingCuttingHeight(cmd.byteValue()); + } + } else if (channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { + if (command instanceof StringType cmd) { + sendAutomowerSettingHeadlightMode(cmd.toString()); + } } else if (channelUID.getId().equals(CHANNEL_COMMAND_CONFIRM_ERROR)) { - sendAutomowerConfirmError(command); + if (command instanceof OnOffType cmd) { + if (cmd == OnOffType.ON) { + sendAutomowerConfirmError(); + updateState(CHANNEL_COMMAND_CONFIRM_ERROR, OnOffType.OFF); + } + } } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { logger.debug("Sending command '{}'", commandName); @@ -312,14 +333,15 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { String param = channelIDSplit[1]; logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, command.toString()); + if (isValidResult(mowerState)) { CalendarTask calendarTask = mowerState.getAttributes().getCalendar().getTasks().get(index); if (calendarTask != null) { if (command instanceof DecimalType cmd) { if ("start".equals(param)) { - calendarTask.setStart((short) cmd.intValue()); + calendarTask.setStart(cmd.shortValue()); } else if ("duration".equals(param)) { - calendarTask.setDuration((short) cmd.intValue()); + calendarTask.setDuration(cmd.shortValue()); } } else if (command instanceof QuantityType cmd) { cmd = cmd.toUnit("min"); @@ -371,37 +393,48 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { } /** - * Sends Settings to the automower + * Sends CuttingHeight Setting to the automower * - * @param command The command that should be sent. E.g. a number for the cuttingHeight channel - * @param channelID The triggering channel + * @param cuttingHeight The cuttingHeight to be sent */ - public void sendAutomowerSettings(Command command, String channelID) { - String[] channelIDSplit = channelID.split("-"); - int index = Integer.parseInt(channelIDSplit[0].substring("calendartasks".length())) - 1; - String param = channelIDSplit[1]; - logger.debug("Sending CalendarTask '{}', index '{}', param '{}', command '{}'", channelID, index, param, - command.toString()); - + public void sendAutomowerStayOutZones(boolean enable, int index) { if (isValidResult(mowerState)) { - Settings settings = mowerState.getAttributes().getSettings(); + MowerStayOutZoneAttributes attributes = new MowerStayOutZoneAttributes(); + attributes.setEnable(enable); + MowerStayOutZone data = new MowerStayOutZone(); + data.setType("stayOutZone"); + data.setId(mowerState.getAttributes().getStayOutZones().getZones().get(index).getId()); + data.setAttributes(attributes); + MowerStayOutZoneRequest request = new MowerStayOutZoneRequest(); + request.setData(data); - if (command instanceof DecimalType cmd) { - if (CHANNEL_SETTING_HEADLIGHT_MODE.equals(channelID)) { - settings.setCuttingHeight((byte) cmd.intValue()); - } - } else if (command instanceof StringType cmd) { - if (CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME.equals(channelID)) { - try { - Headlight headlight = new Headlight(); - headlight.setHeadlightMode(HeadlightMode.valueOf(cmd.toString())); - settings.setHeadlight(headlight); - } catch (IllegalArgumentException e) { - logger.warn("Invalid HeadlightMode: {}, Error: {}", cmd.toString(), e.getMessage()); - } + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerStayOutZones(id, request.getData().getId(), request); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); } + updateAutomowerState(); + } + } + + /** + * Sends CuttingHeight Setting to the automower + * + * @param cuttingHeight The cuttingHeight to be sent + */ + public void sendAutomowerSettingCuttingHeight(byte cuttingHeight) { + if (isValidResult(mowerState)) { + Settings settings = mowerState.getAttributes().getSettings(); + settings.setCuttingHeight(cuttingHeight); + String id = automowerId.get(); try { AutomowerBridge automowerBridge = getAutomowerBridge(); @@ -420,35 +453,61 @@ public void sendAutomowerSettings(Command command, String channelID) { } /** - * Confirm current non fatal error on the mower + * Sends HeadlightMode Setting to the automower * - * @param command The command that was used to trigger the channel + * @param headlightMode Headlight mode as string to be sent */ - public void sendAutomowerConfirmError(Command command) { - logger.debug("Sending ConfirmError '{}'", command.toString()); - if (command instanceof OnOffType cmd) { - if (cmd == OnOffType.ON) { - updateState(CHANNEL_COMMAND_CONFIRM_ERROR, OnOffType.OFF); - if (isValidResult(mowerState) && (mowerState.getAttributes().getCapabilities().canConfirmError()) - && (mowerState.getAttributes().getMower().getIsErrorConfirmable())) { - String id = automowerId.get(); - try { - AutomowerBridge automowerBridge = getAutomowerBridge(); - if (automowerBridge != null) { - automowerBridge.sendAutomowerConfirmError(id); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/conf-error-no-bridge"); - } - } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send ConfirmError to automower: {}, Error: {}", id, e.getMessage()); + public void sendAutomowerSettingHeadlightMode(String headlightMode) { + if (isValidResult(mowerState)) { + try { + Settings settings = mowerState.getAttributes().getSettings(); + Headlight headlight = new Headlight(); + headlight.setHeadlightMode(HeadlightMode.valueOf(headlightMode)); + settings.setHeadlight(headlight); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerSettings(id, settings); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); } - updateAutomowerState(); + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); } + + updateAutomowerState(); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); } } } + /** + * Confirm current non fatal error on the mower + */ + public void sendAutomowerConfirmError() { + logger.debug("Sending ConfirmError"); + if (isValidResult(mowerState) && (mowerState.getAttributes().getCapabilities().canConfirmError()) + && (mowerState.getAttributes().getMower().getIsErrorConfirmable())) { + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerConfirmError(id); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send ConfirmError to automower: {}, Error: {}", id, e.getMessage()); + } + updateAutomowerState(); + } + } + private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index f273f1acd72b1..a339ed3c63874 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -194,9 +194,8 @@ - - Last Position - + + @@ -628,10 +627,17 @@ - + + Location + + Last GPS position of the mower + + + + Location - The channel providing a waypoint of the mower's activity. + A waypoint of the mower's activity From 3f24b27abe1e14c49540769b38cffa1df5e458a5 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 19 Oct 2024 09:03:35 +0200 Subject: [PATCH 20/35] added sendAutomowerWorkArea() Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 60 ++++---- .../internal/bridge/AutomowerBridge.java | 18 ++- .../automowerconnect/AutomowerConnectApi.java | 17 ++- .../automowerconnect/dto/MowerWorkArea.java | 46 ++++++ .../dto/MowerWorkAreaAttributes.java | 37 +++++ .../dto/MowerWorkAreaRequest.java | 28 ++++ .../internal/things/AutomowerHandler.java | 107 ++++++++++++-- .../resources/OH-INF/thing/thing-types.xml | 132 +++++++++--------- 8 files changed, 338 insertions(+), 107 deletions(-) create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java create mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index b9381b3370a6d..b59e02a72dac9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -180,45 +180,45 @@ public class AutomowerBindingConstants { public static final String GROUP_WORKAREAS = ""; // no channel group in use at the moment, we'll possibly // introduce this in a future release public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>( - List.of(GROUP_WORKAREAS + "workareas01-id", GROUP_WORKAREAS + "workareas01-name", - GROUP_WORKAREAS + "workareas01-cutting-height", GROUP_WORKAREAS + "workareas01-enabled", - GROUP_WORKAREAS + "workareas01-progress", GROUP_WORKAREAS + "workareas01-last-time-completed", + List.of(GROUP_WORKAREAS + "workarea01-id", GROUP_WORKAREAS + "workarea01-name", + GROUP_WORKAREAS + "workarea01-cutting-height", GROUP_WORKAREAS + "workarea01-enabled", + GROUP_WORKAREAS + "workarea01-progress", GROUP_WORKAREAS + "workarea01-last-time-completed", - GROUP_WORKAREAS + "workareas02-id", GROUP_WORKAREAS + "workareas02-name", - GROUP_WORKAREAS + "workareas02-cutting-height", GROUP_WORKAREAS + "workareas02-enabled", - GROUP_WORKAREAS + "workareas02-progress", GROUP_WORKAREAS + "workareas02-last-time-completed", + GROUP_WORKAREAS + "workarea02-id", GROUP_WORKAREAS + "workarea02-name", + GROUP_WORKAREAS + "workarea02-cutting-height", GROUP_WORKAREAS + "workarea02-enabled", + GROUP_WORKAREAS + "workarea02-progress", GROUP_WORKAREAS + "workarea02-last-time-completed", - GROUP_WORKAREAS + "workareas03-id", GROUP_WORKAREAS + "workareas03-name", - GROUP_WORKAREAS + "workareas03-cutting-height", GROUP_WORKAREAS + "workareas03-enabled", - GROUP_WORKAREAS + "workareas03-progress", GROUP_WORKAREAS + "workareas03-last-time-completed", + GROUP_WORKAREAS + "workarea03-id", GROUP_WORKAREAS + "workarea03-name", + GROUP_WORKAREAS + "workarea03-cutting-height", GROUP_WORKAREAS + "workarea03-enabled", + GROUP_WORKAREAS + "workarea03-progress", GROUP_WORKAREAS + "workarea03-last-time-completed", - GROUP_WORKAREAS + "workareas04-id", GROUP_WORKAREAS + "workareas04-name", - GROUP_WORKAREAS + "workareas04-cutting-height", GROUP_WORKAREAS + "workareas04-enabled", - GROUP_WORKAREAS + "workareas04-progress", GROUP_WORKAREAS + "workareas04-last-time-completed", + GROUP_WORKAREAS + "workarea04-id", GROUP_WORKAREAS + "workarea04-name", + GROUP_WORKAREAS + "workarea04-cutting-height", GROUP_WORKAREAS + "workarea04-enabled", + GROUP_WORKAREAS + "workarea04-progress", GROUP_WORKAREAS + "workarea04-last-time-completed", - GROUP_WORKAREAS + "workareas05-id", GROUP_WORKAREAS + "workareas05-name", - GROUP_WORKAREAS + "workareas05-cutting-height", GROUP_WORKAREAS + "workareas05-enabled", - GROUP_WORKAREAS + "workareas05-progress", GROUP_WORKAREAS + "workareas05-last-time-completed", + GROUP_WORKAREAS + "workarea05-id", GROUP_WORKAREAS + "workarea05-name", + GROUP_WORKAREAS + "workarea05-cutting-height", GROUP_WORKAREAS + "workarea05-enabled", + GROUP_WORKAREAS + "workarea05-progress", GROUP_WORKAREAS + "workarea05-last-time-completed", - GROUP_WORKAREAS + "workareas06-id", GROUP_WORKAREAS + "workareas06-name", - GROUP_WORKAREAS + "workareas06-cutting-height", GROUP_WORKAREAS + "workareas06-enabled", - GROUP_WORKAREAS + "workareas06-progress", GROUP_WORKAREAS + "workareas06-last-time-completed", + GROUP_WORKAREAS + "workarea06-id", GROUP_WORKAREAS + "workarea06-name", + GROUP_WORKAREAS + "workarea06-cutting-height", GROUP_WORKAREAS + "workarea06-enabled", + GROUP_WORKAREAS + "workarea06-progress", GROUP_WORKAREAS + "workarea06-last-time-completed", - GROUP_WORKAREAS + "workareas07-id", GROUP_WORKAREAS + "workareas07-name", - GROUP_WORKAREAS + "workareas07-cutting-height", GROUP_WORKAREAS + "workareas07-enabled", - GROUP_WORKAREAS + "workareas07-progress", GROUP_WORKAREAS + "workareas07-last-time-completed", + GROUP_WORKAREAS + "workarea07-id", GROUP_WORKAREAS + "workarea07-name", + GROUP_WORKAREAS + "workarea07-cutting-height", GROUP_WORKAREAS + "workarea07-enabled", + GROUP_WORKAREAS + "workarea07-progress", GROUP_WORKAREAS + "workarea07-last-time-completed", - GROUP_WORKAREAS + "workareas08-id", GROUP_WORKAREAS + "workareas08-name", - GROUP_WORKAREAS + "workareas08-cutting-height", GROUP_WORKAREAS + "workareas08-enabled", - GROUP_WORKAREAS + "workareas08-progress", GROUP_WORKAREAS + "workareas08-last-time-completed", + GROUP_WORKAREAS + "workarea08-id", GROUP_WORKAREAS + "workarea08-name", + GROUP_WORKAREAS + "workarea08-cutting-height", GROUP_WORKAREAS + "workarea08-enabled", + GROUP_WORKAREAS + "workarea08-progress", GROUP_WORKAREAS + "workarea08-last-time-completed", - GROUP_WORKAREAS + "workareas09-id", GROUP_WORKAREAS + "workareas09-name", - GROUP_WORKAREAS + "workareas09-cutting-height", GROUP_WORKAREAS + "workareas09-enabled", - GROUP_WORKAREAS + "workareas09-progress", GROUP_WORKAREAS + "workareas09-last-time-completed", + GROUP_WORKAREAS + "workarea09-id", GROUP_WORKAREAS + "workarea09-name", + GROUP_WORKAREAS + "workarea09-cutting-height", GROUP_WORKAREAS + "workarea09-enabled", + GROUP_WORKAREAS + "workarea09-progress", GROUP_WORKAREAS + "workarea09-last-time-completed", - GROUP_WORKAREAS + "workareas10-id", GROUP_WORKAREAS + "workareas10-name", - GROUP_WORKAREAS + "workareas10-cutting-height", GROUP_WORKAREAS + "workareas10-enabled", - GROUP_WORKAREAS + "workareas10-progress", GROUP_WORKAREAS + "workareas10-last-time-completed")); + GROUP_WORKAREAS + "workarea10-id", GROUP_WORKAREAS + "workarea10-name", + GROUP_WORKAREAS + "workarea10-cutting-height", GROUP_WORKAREAS + "workarea10-enabled", + GROUP_WORKAREAS + "workarea10-progress", GROUP_WORKAREAS + "workarea10-last-time-completed")); // Command Channel ids public static final String GROUP_COMMANDS = ""; // no channel group in use at the moment, we'll possibly introduce diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 1951cc14c3e85..baacaadc40a13 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -33,6 +33,7 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettings; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.things.AutomowerCommand; @@ -177,8 +178,21 @@ public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationEx * @param zoneRequest The new zone status * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerStayOutZones(String id, String zoneId, MowerStayOutZoneRequest zoneRequest) + public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneRequest zoneRequest) throws AutomowerCommunicationException { - automowerApi.sendStayOutZones(appKey, authenticate().getAccessToken(), id, zoneId, zoneRequest); + automowerApi.sendStayOutZone(appKey, authenticate().getAccessToken(), id, zoneId, zoneRequest); + } + + /** + * Update a work area setting + * + * @param id The id of the mower + * @param workAreaId The id of the work area + * @param workAreaRequest The new work area status + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerWorkArea(String id, long workAreaId, MowerWorkAreaRequest workAreaRequest) + throws AutomowerCommunicationException { + automowerApi.sendWorkArea(appKey, authenticate().getAccessToken(), id, workAreaId, workAreaRequest); } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 954ce5c1f9856..dfc92a1623812 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -30,6 +30,7 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; @@ -123,7 +124,7 @@ public void sendConfirmError(String appKey, String token, String id) throws Auto checkForError(response, response.getStatus()); } - public void sendStayOutZones(String appKey, String token, String id, String zoneId, + public void sendStayOutZone(String appKey, String token, String id, String zoneId, MowerStayOutZoneRequest zoneRequest) throws AutomowerCommunicationException { String url; url = getBaseUrl() + "/mowers/" + id + "/stayOutZones/" + zoneId; @@ -137,6 +138,20 @@ public void sendStayOutZones(String appKey, String token, String id, String zone checkForError(response, response.getStatus()); } + public void sendWorkArea(String appKey, String token, String id, long workAreaId, + MowerWorkAreaRequest workAreaRequest) throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.PATCH); + + request.content(new StringContentProvider(gson.toJson(workAreaRequest))); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + private ContentResponse executeRequest(String appKey, String token, final Request request) throws AutomowerCommunicationException { request.timeout(10, TimeUnit.SECONDS); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java new file mode 100644 index 0000000000000..9ab8216f84d0e --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkArea.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkArea { + private String type; + private long id; + private MowerWorkAreaAttributes attributes; + + public String getType() { + return type; + } + + public void setId(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setType(String type) { + this.type = type; + } + + public MowerWorkAreaAttributes getAttributes() { + return attributes; + } + + public void setAttributes(MowerWorkAreaAttributes attributes) { + this.attributes = attributes; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java new file mode 100644 index 0000000000000..7544086f84c0a --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaAttributes.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkAreaAttributes { + private byte cuttingHeight; + private boolean enable; + + public byte getCuttingHeight() { + return cuttingHeight; + } + + public void setCuttingHeight(byte cuttingHeight) { + this.cuttingHeight = cuttingHeight; + } + + public boolean getEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java new file mode 100644 index 0000000000000..4bbe99ddb494b --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerWorkAreaRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2024 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; + +/** + * @author MikeTheTux - Initial contribution + */ +public class MowerWorkAreaRequest { + private MowerWorkArea data; + + public MowerWorkArea getData() { + return data; + } + + public void setData(MowerWorkArea data) { + this.data = data; + } +} diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 28d093d518d94..5933639e18661 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -40,6 +40,9 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZone; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkArea; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaAttributes; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; @@ -121,11 +124,28 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else if (CHANNEL_CALENDARTASKS.contains(channelUID.getId())) { sendAutomowerCalendarTask(command, channelUID.getId()); } else if (CHANNEL_STAYOUTZONES.contains(channelUID.getId())) { - if (channelUID.getId().contains("-enabled")) { + String[] channelIDSplit = channelUID.getId().split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("zone".length())) - 1; + if ("enabled".equals(channelIDSplit[0])) { if (command instanceof OnOffType cmd) { - String[] channelIDSplit = channelUID.getId().split("-"); - int index = Integer.parseInt(channelIDSplit[0].substring("zone".length())) - 1; - sendAutomowerStayOutZones(((cmd == OnOffType.ON) ? true : false), index); + sendAutomowerStayOutZone(((cmd == OnOffType.ON) ? true : false), index); + } + } + } else if (CHANNEL_WORKAREAS.contains(channelUID.getId())) { + String[] channelIDSplit = channelUID.getId().split("-"); + int index = Integer.parseInt(channelIDSplit[0].substring("workarea".length())) - 1; + if ("enabled".equals(channelIDSplit[0])) { + if (command instanceof OnOffType cmd) { + sendAutomowerWorkAreaEnable(((cmd == OnOffType.ON) ? true : false), index); + } + } else if ("cutting-height".equals(channelIDSplit[0])) { + if (command instanceof QuantityType cmd) { + cmd = cmd.toUnit("%"); + if (cmd != null) { + sendAutomowerWorkAreaCuttingHeight(cmd.byteValue(), index); + } + } else if (command instanceof DecimalType cmd) { + sendAutomowerWorkAreaCuttingHeight(cmd.byteValue(), index); } } } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT)) { @@ -393,11 +413,12 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { } /** - * Sends CuttingHeight Setting to the automower + * Sends StayOutZone Setting to the automower * - * @param cuttingHeight The cuttingHeight to be sent + * @param enable Zone enabled or disabled + * @param index Index of zone */ - public void sendAutomowerStayOutZones(boolean enable, int index) { + public void sendAutomowerStayOutZone(boolean enable, int index) { if (isValidResult(mowerState)) { MowerStayOutZoneAttributes attributes = new MowerStayOutZoneAttributes(); attributes.setEnable(enable); @@ -412,7 +433,77 @@ public void sendAutomowerStayOutZones(boolean enable, int index) { try { AutomowerBridge automowerBridge = getAutomowerBridge(); if (automowerBridge != null) { - automowerBridge.sendAutomowerStayOutZones(id, request.getData().getId(), request); + automowerBridge.sendAutomowerStayOutZone(id, request.getData().getId(), request); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + + /** + * Sends WorkArea enable Setting to the automower + * + * @param enable WorkArea enabled or disabled + * @param index Index of WorkArea + */ + public void sendAutomowerWorkAreaEnable(boolean enable, int index) { + if (isValidResult(mowerState)) { + MowerWorkAreaAttributes attributes = new MowerWorkAreaAttributes(); + attributes.setEnable(enable); + attributes.setCuttingHeight(mowerState.getAttributes().getWorkAreas().get(index).getCuttingHeight()); + MowerWorkArea data = new MowerWorkArea(); + data.setType("workArea"); + data.setId(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId()); + data.setAttributes(attributes); + MowerWorkAreaRequest request = new MowerWorkAreaRequest(); + request.setData(data); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerWorkArea(id, request.getData().getId(), request); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + } + + updateAutomowerState(); + } + } + + /** + * Sends WorkArea CuttingHeight Setting to the automower + * + * @param enable CuttingHeight of the WorkArea + * @param index Index of WorkArea + */ + public void sendAutomowerWorkAreaCuttingHeight(byte cuttingHeight, int index) { + if (isValidResult(mowerState)) { + MowerWorkAreaAttributes attributes = new MowerWorkAreaAttributes(); + attributes.setEnable(mowerState.getAttributes().getWorkAreas().get(index).isEnabled()); + attributes.setCuttingHeight(cuttingHeight); + MowerWorkArea data = new MowerWorkArea(); + data.setType("workArea"); + data.setId(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId()); + data.setAttributes(attributes); + MowerWorkAreaRequest request = new MowerWorkAreaRequest(); + request.setData(data); + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerWorkArea(id, request.getData().getId(), request); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index a339ed3c63874..a26a7dcbef190 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -281,66 +281,66 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -727,42 +727,42 @@ - + Number Id of the Work Area - + String Name of the work area - + Number:Dimensionless Cutting height in percent. 0-100 - + Switch If the work area is enabled or disabled - + Number:Dimensionless The progress on a work area. EPOS mowers and systematic mowing work areas only. - + DateTime Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. From 34c5d7b3d1b174e0b47d8f8c6c34bbe056286dd0 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 21 Oct 2024 21:22:13 +0200 Subject: [PATCH 21/35] refactored actions Signed-off-by: Michael Weger --- .../internal/AutomowerBindingConstants.java | 55 +-- .../internal/actions/AutomowerActions.java | 65 +++- .../internal/bridge/AutomowerBridge.java | 44 ++- .../internal/things/AutomowerHandler.java | 322 +++++++++++------- .../resources/OH-INF/thing/thing-types.xml | 10 + 5 files changed, 322 insertions(+), 174 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index b59e02a72dac9..185675951391d 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -90,51 +90,56 @@ public class AutomowerBindingConstants { GROUP_CALENDARTASKS + "calendartasks01-wednesday", GROUP_CALENDARTASKS + "calendartasks01-thursday", GROUP_CALENDARTASKS + "calendartasks01-friday", GROUP_CALENDARTASKS + "calendartasks01-saturday", GROUP_CALENDARTASKS + "calendartasks01-sunday", GROUP_CALENDARTASKS + "calendartasks01-workAreaId", - GROUP_CALENDARTASKS + "calendartasks02-start", GROUP_CALENDARTASKS + "calendartasks02-duration", - GROUP_CALENDARTASKS + "calendartasks02-monday", GROUP_CALENDARTASKS + "calendartasks02-tuesday", - GROUP_CALENDARTASKS + "calendartasks02-wednesday", GROUP_CALENDARTASKS + "calendartasks02-thursday", - GROUP_CALENDARTASKS + "calendartasks02-friday", GROUP_CALENDARTASKS + "calendartasks02-saturday", - GROUP_CALENDARTASKS + "calendartasks02-sunday", GROUP_CALENDARTASKS + "calendartasks02-workAreaId", + GROUP_CALENDARTASKS + "calendartasks01-workArea", GROUP_CALENDARTASKS + "calendartasks02-start", + GROUP_CALENDARTASKS + "calendartasks02-duration", GROUP_CALENDARTASKS + "calendartasks02-monday", + GROUP_CALENDARTASKS + "calendartasks02-tuesday", GROUP_CALENDARTASKS + "calendartasks02-wednesday", + GROUP_CALENDARTASKS + "calendartasks02-thursday", GROUP_CALENDARTASKS + "calendartasks02-friday", + GROUP_CALENDARTASKS + "calendartasks02-saturday", GROUP_CALENDARTASKS + "calendartasks02-sunday", + GROUP_CALENDARTASKS + "calendartasks02-workAreaId", GROUP_CALENDARTASKS + "calendartasks02-workArea", GROUP_CALENDARTASKS + "calendartasks03-start", GROUP_CALENDARTASKS + "calendartasks03-duration", GROUP_CALENDARTASKS + "calendartasks03-monday", GROUP_CALENDARTASKS + "calendartasks03-tuesday", GROUP_CALENDARTASKS + "calendartasks03-wednesday", GROUP_CALENDARTASKS + "calendartasks03-thursday", GROUP_CALENDARTASKS + "calendartasks03-friday", GROUP_CALENDARTASKS + "calendartasks03-saturday", GROUP_CALENDARTASKS + "calendartasks03-sunday", GROUP_CALENDARTASKS + "calendartasks03-workAreaId", - GROUP_CALENDARTASKS + "calendartasks04-start", GROUP_CALENDARTASKS + "calendartasks04-duration", - GROUP_CALENDARTASKS + "calendartasks04-monday", GROUP_CALENDARTASKS + "calendartasks04-tuesday", - GROUP_CALENDARTASKS + "calendartasks04-wednesday", GROUP_CALENDARTASKS + "calendartasks04-thursday", - GROUP_CALENDARTASKS + "calendartasks04-friday", GROUP_CALENDARTASKS + "calendartasks04-saturday", - GROUP_CALENDARTASKS + "calendartasks04-sunday", GROUP_CALENDARTASKS + "calendartasks04-workAreaId", + GROUP_CALENDARTASKS + "calendartasks03-workArea", GROUP_CALENDARTASKS + "calendartasks04-start", + GROUP_CALENDARTASKS + "calendartasks04-duration", GROUP_CALENDARTASKS + "calendartasks04-monday", + GROUP_CALENDARTASKS + "calendartasks04-tuesday", GROUP_CALENDARTASKS + "calendartasks04-wednesday", + GROUP_CALENDARTASKS + "calendartasks04-thursday", GROUP_CALENDARTASKS + "calendartasks04-friday", + GROUP_CALENDARTASKS + "calendartasks04-saturday", GROUP_CALENDARTASKS + "calendartasks04-sunday", + GROUP_CALENDARTASKS + "calendartasks04-workAreaId", GROUP_CALENDARTASKS + "calendartasks04-workArea", GROUP_CALENDARTASKS + "calendartasks05-start", GROUP_CALENDARTASKS + "calendartasks05-duration", GROUP_CALENDARTASKS + "calendartasks05-monday", GROUP_CALENDARTASKS + "calendartasks05-tuesday", GROUP_CALENDARTASKS + "calendartasks05-wednesday", GROUP_CALENDARTASKS + "calendartasks05-thursday", GROUP_CALENDARTASKS + "calendartasks05-friday", GROUP_CALENDARTASKS + "calendartasks05-saturday", GROUP_CALENDARTASKS + "calendartasks05-sunday", GROUP_CALENDARTASKS + "calendartasks05-workAreaId", - GROUP_CALENDARTASKS + "calendartasks06-start", GROUP_CALENDARTASKS + "calendartasks06-duration", - GROUP_CALENDARTASKS + "calendartasks06-monday", GROUP_CALENDARTASKS + "calendartasks06-tuesday", - GROUP_CALENDARTASKS + "calendartasks06-wednesday", GROUP_CALENDARTASKS + "calendartasks06-thursday", - GROUP_CALENDARTASKS + "calendartasks06-friday", GROUP_CALENDARTASKS + "calendartasks06-saturday", - GROUP_CALENDARTASKS + "calendartasks06-sunday", GROUP_CALENDARTASKS + "calendartasks06-workAreaId", + GROUP_CALENDARTASKS + "calendartasks05-workArea", GROUP_CALENDARTASKS + "calendartasks06-start", + GROUP_CALENDARTASKS + "calendartasks06-duration", GROUP_CALENDARTASKS + "calendartasks06-monday", + GROUP_CALENDARTASKS + "calendartasks06-tuesday", GROUP_CALENDARTASKS + "calendartasks06-wednesday", + GROUP_CALENDARTASKS + "calendartasks06-thursday", GROUP_CALENDARTASKS + "calendartasks06-friday", + GROUP_CALENDARTASKS + "calendartasks06-saturday", GROUP_CALENDARTASKS + "calendartasks06-sunday", + GROUP_CALENDARTASKS + "calendartasks06-workAreaId", GROUP_CALENDARTASKS + "calendartasks06-workArea", GROUP_CALENDARTASKS + "calendartasks07-start", GROUP_CALENDARTASKS + "calendartasks07-duration", GROUP_CALENDARTASKS + "calendartasks07-monday", GROUP_CALENDARTASKS + "calendartasks07-tuesday", GROUP_CALENDARTASKS + "calendartasks07-wednesday", GROUP_CALENDARTASKS + "calendartasks07-thursday", GROUP_CALENDARTASKS + "calendartasks07-friday", GROUP_CALENDARTASKS + "calendartasks07-saturday", GROUP_CALENDARTASKS + "calendartasks07-sunday", GROUP_CALENDARTASKS + "calendartasks07-workAreaId", - GROUP_CALENDARTASKS + "calendartasks08-start", GROUP_CALENDARTASKS + "calendartasks08-duration", - GROUP_CALENDARTASKS + "calendartasks08-monday", GROUP_CALENDARTASKS + "calendartasks08-tuesday", - GROUP_CALENDARTASKS + "calendartasks08-wednesday", GROUP_CALENDARTASKS + "calendartasks08-thursday", - GROUP_CALENDARTASKS + "calendartasks08-friday", GROUP_CALENDARTASKS + "calendartasks08-saturday", - GROUP_CALENDARTASKS + "calendartasks08-sunday", GROUP_CALENDARTASKS + "calendartasks08-workAreaId", + GROUP_CALENDARTASKS + "calendartasks07-workArea", GROUP_CALENDARTASKS + "calendartasks08-start", + GROUP_CALENDARTASKS + "calendartasks08-duration", GROUP_CALENDARTASKS + "calendartasks08-monday", + GROUP_CALENDARTASKS + "calendartasks08-tuesday", GROUP_CALENDARTASKS + "calendartasks08-wednesday", + GROUP_CALENDARTASKS + "calendartasks08-thursday", GROUP_CALENDARTASKS + "calendartasks08-friday", + GROUP_CALENDARTASKS + "calendartasks08-saturday", GROUP_CALENDARTASKS + "calendartasks08-sunday", + GROUP_CALENDARTASKS + "calendartasks08-workAreaId", GROUP_CALENDARTASKS + "calendartasks08-workArea", GROUP_CALENDARTASKS + "calendartasks09-start", GROUP_CALENDARTASKS + "calendartasks09-duration", GROUP_CALENDARTASKS + "calendartasks09-monday", GROUP_CALENDARTASKS + "calendartasks09-tuesday", GROUP_CALENDARTASKS + "calendartasks09-wednesday", GROUP_CALENDARTASKS + "calendartasks09-thursday", GROUP_CALENDARTASKS + "calendartasks09-friday", GROUP_CALENDARTASKS + "calendartasks09-saturday", GROUP_CALENDARTASKS + "calendartasks09-sunday", GROUP_CALENDARTASKS + "calendartasks09-workAreaId", - GROUP_CALENDARTASKS + "calendartasks10-start", GROUP_CALENDARTASKS + "calendartasks10-duration", - GROUP_CALENDARTASKS + "calendartasks10-monday", GROUP_CALENDARTASKS + "calendartasks10-tuesday", - GROUP_CALENDARTASKS + "calendartasks10-wednesday", GROUP_CALENDARTASKS + "calendartasks10-thursday", - GROUP_CALENDARTASKS + "calendartasks10-friday", GROUP_CALENDARTASKS + "calendartasks10-saturday", - GROUP_CALENDARTASKS + "calendartasks10-sunday", GROUP_CALENDARTASKS + "calendartasks10-workAreaId")); + GROUP_CALENDARTASKS + "calendartasks09-workArea", GROUP_CALENDARTASKS + "calendartasks10-start", + GROUP_CALENDARTASKS + "calendartasks10-duration", GROUP_CALENDARTASKS + "calendartasks10-monday", + GROUP_CALENDARTASKS + "calendartasks10-tuesday", GROUP_CALENDARTASKS + "calendartasks10-wednesday", + GROUP_CALENDARTASKS + "calendartasks10-thursday", GROUP_CALENDARTASKS + "calendartasks10-friday", + GROUP_CALENDARTASKS + "calendartasks10-saturday", GROUP_CALENDARTASKS + "calendartasks10-sunday", + GROUP_CALENDARTASKS + "calendartasks10-workAreaId", GROUP_CALENDARTASKS + "calendartasks10-workArea")); // Position Channels ids public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index fa509e99dd077..6b9388b03aefc 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -14,6 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.things.AutomowerCommand; import org.openhab.binding.automower.internal.things.AutomowerHandler; import org.openhab.core.automation.annotation.ActionInput; @@ -146,33 +147,75 @@ public static void confirmError(ThingActions actions) { ((AutomowerActions) actions).confirmError(); } - @RuleAction(label = "@text/action-set-cutting-height-label", description = "@text/action-set-cutting-height-desc") - public void setCuttingHeight( - @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight) { + @RuleAction(label = "@text/action-set-Settings-label", description = "@text/action-set-Settings-desc") + public void setSettings( + @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight, + @ActionInput(name = "headlight-mode", label = "@text/action-input-headlight-mode-label", description = "@text/action-input-headlight-mode-desc") String headlightMode) { AutomowerHandler automowerHandler = handler; if (automowerHandler == null) { logger.warn("Automower Action service ThingHandler is null!"); } else { - automowerHandler.sendAutomowerSettingCuttingHeight(cuttingHeight); + try { + automowerHandler.sendAutomowerSettings(cuttingHeight, HeadlightMode.valueOf(headlightMode)); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); + } } } - public static void setCuttingHeight(ThingActions actions, byte cuttingHeight) { - ((AutomowerActions) actions).setCuttingHeight(cuttingHeight); + public static void setSettings(ThingActions actions, byte cuttingHeight, String headlightMode) { + ((AutomowerActions) actions).setSettings(cuttingHeight, headlightMode); } @RuleAction(label = "@text/action-set-headlight-mode-label", description = "@text/action-set-headlight-mode-desc") - public void setHeadlightMode( - @ActionInput(name = "headlight-mode", label = "@text/action-input-headlight-mode-label", description = "@text/action-input-headlight-mode-desc") String headlightMode) { + public void setWorkArea( + @ActionInput(name = "workarea-id", label = "@text/action-input-workarea-id-label", description = "@text/action-input-workarea-id-desc") long workAreaId, + @ActionInput(name = "enable", label = "@text/action-input-enable-label", description = "@text/action-input-enable-desc") boolean enable, + @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerWorkArea(workAreaId, enable, cuttingHeight); + } + } + + public static void setWorkArea(ThingActions actions, long workAreaId, boolean enable, byte cuttingHeight) { + ((AutomowerActions) actions).setWorkArea(workAreaId, enable, cuttingHeight); + } + + @RuleAction(label = "@text/action-set-stayoutzone-label", description = "@text/action-set-stayoutzone-desc") + public void setStayOutZone( + @ActionInput(name = "zone-id", label = "@text/action-input-zone-id-label", description = "@text/action-input-zone-id-desc") String zoneId, + @ActionInput(name = "enable", label = "@text/action-input-enable-label", description = "@text/action-input-enable-desc") boolean enable) { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerStayOutZone(zoneId, enable); + } + } + + public static void setStayOutZone(ThingActions actions, String zoneId, boolean enable) { + ((AutomowerActions) actions).setStayOutZone(zoneId, enable); + } + + @RuleAction(label = "@text/action-set-calendartask-label", description = "@text/action-set-calendartask-desc") + public void setCalendarTask(Long workAreaId, short[] start, short[] duration, boolean[] monday, boolean[] tuesday, + boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, boolean[] sunday) { AutomowerHandler automowerHandler = handler; if (automowerHandler == null) { logger.warn("Automower Action service ThingHandler is null!"); } else { - automowerHandler.sendAutomowerSettingHeadlightMode(headlightMode); + automowerHandler.sendAutomowerCalendarTask(workAreaId, start, duration, monday, tuesday, wednesday, + thursday, friday, saturday, sunday); } } - public static void setHeadlightMode(ThingActions actions, String headlightMode) { - ((AutomowerActions) actions).setHeadlightMode(headlightMode); + public static void setCalendarTask(ThingActions actions, Long workAreaId, short[] start, short[] duration, + boolean[] monday, boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, + boolean[] saturday, boolean[] sunday) { + ((AutomowerActions) actions).setCalendarTask(workAreaId, start, duration, monday, tuesday, wednesday, thursday, + friday, saturday, sunday); } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index baacaadc40a13..2d2201e66d0cc 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.time.Instant; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledExecutorService; @@ -32,7 +31,11 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerListResult; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettings; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerSettingsRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZone; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkArea; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaAttributes; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; @@ -124,21 +127,20 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma * as provided when reading the channel * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long workAreaId, CalendarTask calendarTask) - throws AutomowerCommunicationException { - List tasks = new ArrayList<>(); - tasks.add(calendarTask); + public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long workAreaId, + List calendarTaskArray) throws AutomowerCommunicationException { Calendar calendar = new Calendar(); - calendar.setTasks(tasks); + calendar.setTasks(calendarTaskArray); MowerCalendar mowerCalendar = new MowerCalendar(); mowerCalendar.setType("calendar"); mowerCalendar.setAttributes(calendar); - MowerCalendardRequest request = new MowerCalendardRequest(); - request.setData(mowerCalendar); + MowerCalendardRequest calendarRequest = new MowerCalendardRequest(); + calendarRequest.setData(mowerCalendar); - automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, hasWorkAreas, workAreaId, request); + automowerApi.sendCalendar(appKey, authenticate().getAccessToken(), id, hasWorkAreas, workAreaId, + calendarRequest); } /** @@ -154,10 +156,10 @@ public void sendAutomowerSettings(String id, Settings settings) throws Automower mowerSettings.setType("settings"); mowerSettings.setAttributes(settings); - MowerSettingsRequest request = new MowerSettingsRequest(); - request.setData(mowerSettings); + MowerSettingsRequest settingsRequest = new MowerSettingsRequest(); + settingsRequest.setData(mowerSettings); - automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, request); + automowerApi.sendSettings(appKey, authenticate().getAccessToken(), id, settingsRequest); } /** @@ -178,8 +180,15 @@ public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationEx * @param zoneRequest The new zone status * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneRequest zoneRequest) + public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneAttributes zoneAttributes) throws AutomowerCommunicationException { + MowerStayOutZone zoneData = new MowerStayOutZone(); + zoneData.setType("stayOutZone"); + zoneData.setId(zoneId); + zoneData.setAttributes(zoneAttributes); + MowerStayOutZoneRequest zoneRequest = new MowerStayOutZoneRequest(); + zoneRequest.setData(zoneData); + automowerApi.sendStayOutZone(appKey, authenticate().getAccessToken(), id, zoneId, zoneRequest); } @@ -191,8 +200,15 @@ public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneR * @param workAreaRequest The new work area status * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ - public void sendAutomowerWorkArea(String id, long workAreaId, MowerWorkAreaRequest workAreaRequest) + public void sendAutomowerWorkArea(String id, long workAreaId, MowerWorkAreaAttributes workAreaAttributes) throws AutomowerCommunicationException { + MowerWorkArea workAreaData = new MowerWorkArea(); + workAreaData.setType("workArea"); + workAreaData.setId(workAreaId); + workAreaData.setAttributes(workAreaAttributes); + MowerWorkAreaRequest workAreaRequest = new MowerWorkAreaRequest(); + workAreaRequest.setData(workAreaData); + automowerApi.sendWorkArea(appKey, authenticate().getAccessToken(), id, workAreaId, workAreaRequest); } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 5933639e18661..c42a4bd98532b 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -18,6 +18,7 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -37,12 +38,8 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Headlight; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; -import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZone; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneAttributes; -import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerStayOutZoneRequest; -import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkArea; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaAttributes; -import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Position; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.RestrictedReason; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Settings; @@ -128,7 +125,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { int index = Integer.parseInt(channelIDSplit[0].substring("zone".length())) - 1; if ("enabled".equals(channelIDSplit[0])) { if (command instanceof OnOffType cmd) { - sendAutomowerStayOutZone(((cmd == OnOffType.ON) ? true : false), index); + sendAutomowerStayOutZone(index, ((cmd == OnOffType.ON) ? true : false)); } } } else if (CHANNEL_WORKAREAS.contains(channelUID.getId())) { @@ -136,25 +133,25 @@ public void handleCommand(ChannelUID channelUID, Command command) { int index = Integer.parseInt(channelIDSplit[0].substring("workarea".length())) - 1; if ("enabled".equals(channelIDSplit[0])) { if (command instanceof OnOffType cmd) { - sendAutomowerWorkAreaEnable(((cmd == OnOffType.ON) ? true : false), index); + sendAutomowerWorkAreaEnable(index, ((cmd == OnOffType.ON) ? true : false)); } } else if ("cutting-height".equals(channelIDSplit[0])) { if (command instanceof QuantityType cmd) { cmd = cmd.toUnit("%"); if (cmd != null) { - sendAutomowerWorkAreaCuttingHeight(cmd.byteValue(), index); + sendAutomowerWorkAreaCuttingHeight(index, cmd.byteValue()); } } else if (command instanceof DecimalType cmd) { - sendAutomowerWorkAreaCuttingHeight(cmd.byteValue(), index); + sendAutomowerWorkAreaCuttingHeight(index, cmd.byteValue()); } } } else if (channelUID.getId().equals(CHANNEL_SETTING_CUTTING_HEIGHT)) { if (command instanceof DecimalType cmd) { - sendAutomowerSettingCuttingHeight(cmd.byteValue()); + sendAutomowerSettingsCuttingHeight(cmd.byteValue()); } } else if (channelUID.getId().equals(CHANNEL_SETTING_HEADLIGHT_MODE)) { if (command instanceof StringType cmd) { - sendAutomowerSettingHeadlightMode(cmd.toString()); + sendAutomowerSettingsHeadlightMode(cmd.toString()); } } else if (channelUID.getId().equals(CHANNEL_COMMAND_CONFIRM_ERROR)) { if (command instanceof OnOffType cmd) { @@ -336,11 +333,57 @@ public void sendAutomowerCommand(AutomowerCommand command, long commandDurationM updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send command to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send Command to automower: {}, Error: {}", id, e.getMessage()); } updateAutomowerState(); } + /** + * Sends a CalendarTask to the automower + * + * @param command The command that should be sent. E.g. a duration in min for the Start channel + * @param channelID The triggering channel + */ + public void sendAutomowerCalendarTask(Long workAreaId, short[] start, short[] duration, boolean[] monday, + boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, + boolean[] sunday) { + if (isValidResult(mowerState)) { + + List calendarTaskArray = new ArrayList<>(); + + for (int i = 0; (i < start.length) && (i < duration.length) && (i < monday.length) && (i < tuesday.length) + && (i < wednesday.length) && (i < thursday.length) && (i < friday.length) && (i < saturday.length) + && (i < sunday.length); i++) { + CalendarTask calendarTask = new CalendarTask(); + calendarTask.setStart(start[i]); + calendarTask.setDuration(duration[i]); + calendarTask.setMonday(monday[i]); + calendarTask.setTuesday(tuesday[i]); + calendarTask.setWednesday(wednesday[i]); + calendarTask.setThursday(thursday[i]); + calendarTask.setFriday(friday[i]); + calendarTask.setSaturday(saturday[i]); + calendarTask.setSunday(sunday[i]); + + calendarTaskArray.add(calendarTask); + } + + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerCalendarTask(id, + mowerState.getAttributes().getCapabilities().hasWorkAreas(), workAreaId, calendarTaskArray); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send CalendarTask to automower: {}, Error: {}", id, e.getMessage()); + } + } + } + /** * Sends a CalendarTask to the automower * @@ -355,8 +398,27 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { command.toString()); if (isValidResult(mowerState)) { - CalendarTask calendarTask = mowerState.getAttributes().getCalendar().getTasks().get(index); - if (calendarTask != null) { + List calendarTasksFiltered; + List calendarTasksAll = mowerState.getAttributes().getCalendar().getTasks(); + if (mowerState.getAttributes().getCapabilities().hasWorkAreas()) { + // only set the Tasks of the current WorkArea + calendarTasksFiltered = new ArrayList<>(); + for (CalendarTask calendarTask : calendarTasksAll) { + if (calendarTask.getWorkAreaId().equals(calendarTasksAll.get(index).getWorkAreaId())) { + calendarTasksFiltered.add(calendarTask); + } + } + } else { + calendarTasksFiltered = calendarTasksAll; + } + + if (calendarTasksFiltered.get(index) != null) { + CalendarTask calendarTask = calendarTasksAll.get(index); // possible race condition: cache is update in + // the background via cyclic tasks before the + // new request is sent out - deep clone of + // object required + // Different handling required - hasWorkAreas + // ON/OFF if (command instanceof DecimalType cmd) { if ("start".equals(param)) { calendarTask.setStart(cmd.shortValue()); @@ -393,19 +455,18 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { } String id = automowerId.get(); - try { AutomowerBridge automowerBridge = getAutomowerBridge(); if (automowerBridge != null) { automowerBridge.sendAutomowerCalendarTask(id, mowerState.getAttributes().getCapabilities().hasWorkAreas(), - calendarTask.getWorkAreaId(), calendarTask); + calendarTask.getWorkAreaId(), calendarTasksFiltered); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send CalendarTask to automower: {}, Error: {}", id, e.getMessage()); } } } @@ -415,31 +476,39 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { /** * Sends StayOutZone Setting to the automower * - * @param enable Zone enabled or disabled * @param index Index of zone + * @param enable Zone enabled or disabled + * */ - public void sendAutomowerStayOutZone(boolean enable, int index) { + public void sendAutomowerStayOutZone(int index, boolean enable) { + if (isValidResult(mowerState)) { + sendAutomowerStayOutZone(mowerState.getAttributes().getStayOutZones().getZones().get(index).getId(), + enable); + } + } + + /** + * Sends StayOutZone Setting to the automower + * + * @param zoneId Id of zone + * @param enable Zone enabled or disabled + */ + public void sendAutomowerStayOutZone(String zoneId, boolean enable) { if (isValidResult(mowerState)) { MowerStayOutZoneAttributes attributes = new MowerStayOutZoneAttributes(); attributes.setEnable(enable); - MowerStayOutZone data = new MowerStayOutZone(); - data.setType("stayOutZone"); - data.setId(mowerState.getAttributes().getStayOutZones().getZones().get(index).getId()); - data.setAttributes(attributes); - MowerStayOutZoneRequest request = new MowerStayOutZoneRequest(); - request.setData(data); String id = automowerId.get(); try { AutomowerBridge automowerBridge = getAutomowerBridge(); if (automowerBridge != null) { - automowerBridge.sendAutomowerStayOutZone(id, request.getData().getId(), request); + automowerBridge.sendAutomowerStayOutZone(id, zoneId, attributes); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send StayOutZone to automower: {}, Error: {}", id, e.getMessage()); } updateAutomowerState(); @@ -449,32 +518,60 @@ public void sendAutomowerStayOutZone(boolean enable, int index) { /** * Sends WorkArea enable Setting to the automower * + * @param index Index of WorkArea * @param enable WorkArea enabled or disabled + * + */ + public void sendAutomowerWorkAreaEnable(int index, boolean enable) { + if (isValidResult(mowerState)) { + sendAutomowerWorkArea(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId(), enable, + mowerState.getAttributes().getWorkAreas().get(index).getCuttingHeight()); + } + } + + /** + * Sends WorkArea CuttingHeight Setting to the automower + * * @param index Index of WorkArea + * @param cuttingHeight CuttingHeight of the WorkArea + * */ - public void sendAutomowerWorkAreaEnable(boolean enable, int index) { + public void sendAutomowerWorkAreaCuttingHeight(int index, byte cuttingHeight) { if (isValidResult(mowerState)) { - MowerWorkAreaAttributes attributes = new MowerWorkAreaAttributes(); - attributes.setEnable(enable); - attributes.setCuttingHeight(mowerState.getAttributes().getWorkAreas().get(index).getCuttingHeight()); - MowerWorkArea data = new MowerWorkArea(); - data.setType("workArea"); - data.setId(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId()); - data.setAttributes(attributes); - MowerWorkAreaRequest request = new MowerWorkAreaRequest(); - request.setData(data); + sendAutomowerWorkArea(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId(), + mowerState.getAttributes().getWorkAreas().get(index).isEnabled(), cuttingHeight); + } + } + + /** + * Sends WorkArea Settings to the automower + * + * @param workAreaId Id of WorkArea + * @param enable CuttingHeight of the WorkArea + * @param cuttingHeight CuttingHeight of the WorkArea + */ + public void sendAutomowerWorkArea(long workAreaId, boolean enable, byte cuttingHeight) { + if (isValidResult(mowerState)) { + // update local cache ... + WorkArea workArea = getWorkAreaById(mowerState, workAreaId); + workArea.setEnabled(enable); + workArea.setCuttingHeight(cuttingHeight); + // ... as well as request + MowerWorkAreaAttributes workAreaAttributes = new MowerWorkAreaAttributes(); + workAreaAttributes.setEnable(enable); + workAreaAttributes.setCuttingHeight(cuttingHeight); String id = automowerId.get(); try { AutomowerBridge automowerBridge = getAutomowerBridge(); if (automowerBridge != null) { - automowerBridge.sendAutomowerWorkArea(id, request.getData().getId(), request); + automowerBridge.sendAutomowerWorkArea(id, workAreaId, workAreaAttributes); } else { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send WorkArea to automower: {}, Error: {}", id, e.getMessage()); } updateAutomowerState(); @@ -482,49 +579,49 @@ public void sendAutomowerWorkAreaEnable(boolean enable, int index) { } /** - * Sends WorkArea CuttingHeight Setting to the automower + * Sends CuttingHeight Setting to the automower * - * @param enable CuttingHeight of the WorkArea - * @param index Index of WorkArea + * @param cuttingHeight The cuttingHeight to be sent */ - public void sendAutomowerWorkAreaCuttingHeight(byte cuttingHeight, int index) { + public void sendAutomowerSettingsCuttingHeight(byte cuttingHeight) { if (isValidResult(mowerState)) { - MowerWorkAreaAttributes attributes = new MowerWorkAreaAttributes(); - attributes.setEnable(mowerState.getAttributes().getWorkAreas().get(index).isEnabled()); - attributes.setCuttingHeight(cuttingHeight); - MowerWorkArea data = new MowerWorkArea(); - data.setType("workArea"); - data.setId(mowerState.getAttributes().getWorkAreas().get(index).getWorkAreaId()); - data.setAttributes(attributes); - MowerWorkAreaRequest request = new MowerWorkAreaRequest(); - request.setData(data); + sendAutomowerSettings(cuttingHeight, + mowerState.getAttributes().getSettings().getHeadlight().getHeadlightMode()); + } + } - String id = automowerId.get(); + /** + * Sends HeadlightMode Setting to the automower + * + * @param headlightMode Headlight mode as string to be sent + */ + public void sendAutomowerSettingsHeadlightMode(String headlightMode) { + if (isValidResult(mowerState)) { try { - AutomowerBridge automowerBridge = getAutomowerBridge(); - if (automowerBridge != null) { - automowerBridge.sendAutomowerWorkArea(id, request.getData().getId(), request); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/conf-error-no-bridge"); - } - } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + sendAutomowerSettings(mowerState.getAttributes().getSettings().getCuttingHeight(), + HeadlightMode.valueOf(headlightMode)); + } catch (IllegalArgumentException e) { + logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); } - - updateAutomowerState(); } } /** - * Sends CuttingHeight Setting to the automower + * Sends a set of Settings to the automower * * @param cuttingHeight The cuttingHeight to be sent + * @param headlightMode Headlight mode as string to be sent */ - public void sendAutomowerSettingCuttingHeight(byte cuttingHeight) { + public void sendAutomowerSettings(byte cuttingHeight, HeadlightMode headlightMode) { if (isValidResult(mowerState)) { - Settings settings = mowerState.getAttributes().getSettings(); + Settings settings = new Settings(); settings.setCuttingHeight(cuttingHeight); + Headlight headlight = new Headlight(); + headlight.setHeadlightMode(headlightMode); + + // update local cache ... + mowerState.getAttributes().getSettings().setCuttingHeight(cuttingHeight); + mowerState.getAttributes().getSettings().getHeadlight().setHeadlightMode(headlightMode); String id = automowerId.get(); try { @@ -536,46 +633,13 @@ public void sendAutomowerSettingCuttingHeight(byte cuttingHeight) { "@text/conf-error-no-bridge"); } } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); + logger.warn("Unable to send SettingCuttingHeight to automower: {}, Error: {}", id, e.getMessage()); } updateAutomowerState(); } } - /** - * Sends HeadlightMode Setting to the automower - * - * @param headlightMode Headlight mode as string to be sent - */ - public void sendAutomowerSettingHeadlightMode(String headlightMode) { - if (isValidResult(mowerState)) { - try { - Settings settings = mowerState.getAttributes().getSettings(); - Headlight headlight = new Headlight(); - headlight.setHeadlightMode(HeadlightMode.valueOf(headlightMode)); - settings.setHeadlight(headlight); - - String id = automowerId.get(); - try { - AutomowerBridge automowerBridge = getAutomowerBridge(); - if (automowerBridge != null) { - automowerBridge.sendAutomowerSettings(id, settings); - } else { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/conf-error-no-bridge"); - } - } catch (AutomowerCommunicationException e) { - logger.warn("Unable to send calendar to automower: {}, Error: {}", id, e.getMessage()); - } - - updateAutomowerState(); - } catch (IllegalArgumentException e) { - logger.warn("Invalid HeadlightMode: {}, Error: {}", headlightMode, e.getMessage()); - } - } - } - /** * Confirm current non fatal error on the mower */ @@ -603,11 +667,13 @@ private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } - private @Nullable String workAreaName(Mower mower, long workAreaId) { - List workAreas = mower.getAttributes().getWorkAreas(); - for (int i = 0; i < workAreas.size(); i++) { - if (workAreas.get(i).getWorkAreaId() == workAreaId) { - return workAreas.get(i).getName(); + private @Nullable WorkArea getWorkAreaById(@Nullable Mower mower, long workAreaId) { + if (mower != null) { + List workAreas = mower.getAttributes().getWorkAreas(); + for (WorkArea workArea : workAreas) { + if (workArea.getWorkAreaId() == workAreaId) { + return workArea; + } } } return null; @@ -629,12 +695,18 @@ private void updateChannelState(@Nullable Mower mower) { new StringType(restrictedState(mower.getAttributes().getPlanner().getRestrictedReason()))); } - updateState(CHANNEL_STATUS_WORK_AREA_ID, new DecimalType(mower.getAttributes().getMower().getWorkAreaId())); - if (workAreaName(mower, mower.getAttributes().getMower().getWorkAreaId()) == null) { - updateState(CHANNEL_STATUS_WORK_AREA, UnDefType.NULL); + Long workAreaId = mower.getAttributes().getMower().getWorkAreaId(); + if (workAreaId != null) { + updateState(CHANNEL_STATUS_WORK_AREA_ID, + new DecimalType(mower.getAttributes().getMower().getWorkAreaId())); + WorkArea workArea = getWorkAreaById(mower, workAreaId); + if ((workArea != null) && (workArea.getName() != null)) { + updateState(CHANNEL_STATUS_WORK_AREA, new StringType(workArea.getName())); + } else { + updateState(CHANNEL_STATUS_WORK_AREA, UnDefType.NULL); + } } else { - updateState(CHANNEL_STATUS_WORK_AREA, - new StringType(workAreaName(mower, mower.getAttributes().getMower().getWorkAreaId()))); + updateState(CHANNEL_STATUS_WORK_AREA_ID, UnDefType.NULL); } updateState(CHANNEL_STATUS_LAST_UPDATE, new DateTimeType( @@ -749,8 +821,15 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_CALENDARTASKS.get(j++), OnOffType.from(calendarTasks.get(i).getSunday())); if (calendarTasks.get(i).getWorkAreaId() == null) { updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); } else { updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); + WorkArea workArea = getWorkAreaById(mower, calendarTasks.get(i).getWorkAreaId()); + if (workArea != null) { + updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType(workArea.getName())); + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); + } } } // clear remaining channels @@ -773,25 +852,20 @@ private void updateChannelState(@Nullable Mower mower) { List workAreas = mower.getAttributes().getWorkAreas(); j = 0; for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { - - // logger.warn("workAreas.size(): {}, getName(): {}", workAreas.size(), workAreas.get(i).getName()); - - updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workAreas.get(i).getWorkAreaId())); - updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workAreas.get(i).getName())); - updateState(CHANNEL_WORKAREAS.get(j++), - new QuantityType<>(workAreas.get(i).getCuttingHeight(), Units.PERCENT)); - updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workAreas.get(i).isEnabled())); - if (workAreas.get(i).getProgress() != null) { - updateState(CHANNEL_WORKAREAS.get(j++), - new QuantityType<>(workAreas.get(i).getProgress(), Units.PERCENT)); + WorkArea workArea = workAreas.get(i); + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workArea.getWorkAreaId())); + updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); + updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workArea.getCuttingHeight(), Units.PERCENT)); + updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workArea.isEnabled())); + if (workArea.getProgress() != null) { + updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workArea.getProgress(), Units.PERCENT)); } else { updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); } - if ((workAreas.get(i).getLastTimeCompleted() != null) - && (workAreas.get(i).getLastTimeCompleted() != 0)) { + if ((workArea.getLastTimeCompleted() != null) && (workArea.getLastTimeCompleted() != 0)) { updateState(CHANNEL_WORKAREAS.get(j++), - new DateTimeType(toZonedDateTime(workAreas.get(i).getLastTimeCompleted(), mowerZoneId))); + new DateTimeType(toZonedDateTime(workArea.getLastTimeCompleted(), mowerZoneId))); } else { updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); } diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index a26a7dcbef190..ca5d2f47c4eef 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -84,6 +84,7 @@ + @@ -95,6 +96,7 @@ + @@ -106,6 +108,7 @@ + @@ -117,6 +120,7 @@ + @@ -128,6 +132,7 @@ + @@ -139,6 +144,7 @@ + @@ -150,6 +156,7 @@ + @@ -161,6 +168,7 @@ + @@ -172,6 +180,7 @@ + @@ -183,6 +192,7 @@ + From be2285b9c294ab65c8556f870685c14b5b4dddf8 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 21 Oct 2024 21:38:15 +0200 Subject: [PATCH 22/35] clean-ups Signed-off-by: Michael Weger --- .../internal/things/AutomowerHandler.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index c42a4bd98532b..c79725b160ce5 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -35,6 +35,7 @@ import org.openhab.binding.automower.internal.bridge.AutomowerBridge; import org.openhab.binding.automower.internal.bridge.AutomowerBridgeHandler; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.CalendarTask; +import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Capabilities; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Headlight; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.HeadlightMode; import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.Mower; @@ -478,7 +479,6 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { * * @param index Index of zone * @param enable Zone enabled or disabled - * */ public void sendAutomowerStayOutZone(int index, boolean enable) { if (isValidResult(mowerState)) { @@ -494,6 +494,7 @@ public void sendAutomowerStayOutZone(int index, boolean enable) { * @param enable Zone enabled or disabled */ public void sendAutomowerStayOutZone(String zoneId, boolean enable) { + logger.debug("Sending StayOutZone: zoneId {}, enable {}", zoneId, enable); if (isValidResult(mowerState)) { MowerStayOutZoneAttributes attributes = new MowerStayOutZoneAttributes(); attributes.setEnable(enable); @@ -551,6 +552,7 @@ public void sendAutomowerWorkAreaCuttingHeight(int index, byte cuttingHeight) { * @param cuttingHeight CuttingHeight of the WorkArea */ public void sendAutomowerWorkArea(long workAreaId, boolean enable, byte cuttingHeight) { + logger.debug("Sending WorkArea: workAreaId {}, enable {}, cuttingHeight {}", workAreaId, enable, cuttingHeight); if (isValidResult(mowerState)) { // update local cache ... WorkArea workArea = getWorkAreaById(mowerState, workAreaId); @@ -613,6 +615,7 @@ public void sendAutomowerSettingsHeadlightMode(String headlightMode) { * @param headlightMode Headlight mode as string to be sent */ public void sendAutomowerSettings(byte cuttingHeight, HeadlightMode headlightMode) { + logger.debug("Sending Settings: cuttingHeight {}, headlightMode {}", cuttingHeight, headlightMode.toString()); if (isValidResult(mowerState)) { Settings settings = new Settings(); settings.setCuttingHeight(cuttingHeight); @@ -791,20 +794,6 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STAYOUTZONES_DIRTY, OnOffType.from(mower.getAttributes().getStayOutZones().isDirty())); - /* - * updateState(CHANNEL_CALENDAR_TASKS, - * new StringType(gson.toJson(mower.getAttributes().getCalendar().getTasks()))); - * - * - start - * - duration - * - monday - * - tuesday - * - wednesday - * - thursday - * - friday - * - saturday - * - sunday - */ List calendarTasks = mower.getAttributes().getCalendar().getTasks(); int j = 0; for (int i = 0; i < calendarTasks.size() && j < CHANNEL_CALENDARTASKS.size(); i++) { @@ -887,16 +876,17 @@ private void initializeProperties(@Nullable Mower mower) { properties.put(AutomowerBindingConstants.AUTOMOWER_MODEL, mower.getAttributes().getSystem().getModel()); properties.put(AutomowerBindingConstants.AUTOMOWER_NAME, mower.getAttributes().getSystem().getName()); + Capabilities capabilities = mower.getAttributes().getCapabilities(); properties.put(AutomowerBindingConstants.AUTOMOWER_CAN_CONFIRM_ERROR, - (mower.getAttributes().getCapabilities().canConfirmError() ? "yes" : "no")); + (capabilities.canConfirmError() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_HEADLIGHTS, - (mower.getAttributes().getCapabilities().hasHeadlights() ? "yes" : "no")); + (capabilities.hasHeadlights() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_POSITION, - (mower.getAttributes().getCapabilities().hasPosition() ? "yes" : "no")); + (capabilities.hasPosition() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_STAY_OUT_ZONES, - (mower.getAttributes().getCapabilities().hasStayOutZones() ? "yes" : "no")); + (capabilities.hasStayOutZones() ? "yes" : "no")); properties.put(AutomowerBindingConstants.AUTOMOWER_HAS_WORK_AREAS, - (mower.getAttributes().getCapabilities().hasWorkAreas() ? "yes" : "no")); + (capabilities.hasWorkAreas() ? "yes" : "no")); updateProperties(properties); } From 119a33d1c7e709386c342c23226c5c9dd645862c Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 21 Oct 2024 21:48:07 +0200 Subject: [PATCH 23/35] updated changelog Signed-off-by: Michael Weger --- .../rest/api/automowerconnect/dto/Action.java | 2 +- .../automowerconnect/dto/Capabilities.java | 2 +- .../api/automowerconnect/dto/Headlight.java | 2 +- .../automowerconnect/dto/HeadlightMode.java | 2 +- .../automowerconnect/dto/InactiveReason.java | 2 +- .../automowerconnect/dto/MowerCalendar.java | 2 +- .../dto/MowerCalendardRequest.java | 2 +- .../automowerconnect/dto/MowerSettings.java | 2 +- .../dto/MowerSettingsRequest.java | 2 +- .../api/automowerconnect/dto/Settings.java | 2 +- .../api/automowerconnect/dto/Statistics.java | 2 +- .../api/automowerconnect/dto/StayOutZone.java | 2 +- .../automowerconnect/dto/StayOutZones.java | 2 +- .../api/automowerconnect/dto/WorkArea.java | 2 +- .../rest/api/automowerconnect/export.json.txt | 422 ------------------ 15 files changed, 14 insertions(+), 436 deletions(-) delete mode 100644 bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java index ba1db02fb481d..9717f3fba7eae 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Action.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public enum Action { NOT_ACTIVE, diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java index 58b5f4cedc87c..f077b1682cde4 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Capabilities.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class Capabilities { private boolean canConfirmError; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java index bd4d597c15b9e..4a9e02a70476c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Headlight.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class Headlight { private HeadlightMode mode; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java index d32008c79eff8..a8d339a603d54 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/HeadlightMode.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public enum HeadlightMode { ALWAYS_ON, diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java index 44d44cbbcc16c..c70e32e3ccf66 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/InactiveReason.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public enum InactiveReason { NONE, diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java index f7b20c5000661..0dfc738c7a5ad 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendar.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class MowerCalendar { private String type; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java index 8b28b465dbcf2..55a7c74db42db 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerCalendardRequest.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class MowerCalendardRequest { private MowerCalendar data; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java index 7025db2b6b372..d4a7d5a7edd3b 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettings.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class MowerSettings { private String type; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java index d05673073b673..39a15f00043d4 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/MowerSettingsRequest.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class MowerSettingsRequest { private MowerSettings data; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java index 10da3586af961..837862e82999a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Settings.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class Settings { private byte cuttingHeight; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java index 3b9f1a0c8a913..63ca68fcd38ac 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class Statistics { private long cuttingBladeUsageTime; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java index f21d893e9b77e..2be44d338e4d6 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZone.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class StayOutZone { private String id; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java index 8a58864a4bdb8..dde68d5c25604 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/StayOutZones.java @@ -16,7 +16,7 @@ import java.util.List; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class StayOutZones { private Boolean dirty; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java index 624e99dbbe738..4eca587d6f2ad 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/WorkArea.java @@ -13,7 +13,7 @@ package org.openhab.binding.automower.internal.rest.api.automowerconnect.dto; /** - * @author Markus Pfleger - Initial contribution + * @author MikeTheTux - Initial contribution */ public class WorkArea { private long workAreaId; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt deleted file mode 100644 index 4b01c69933359..0000000000000 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/export.json.txt +++ /dev/null @@ -1,422 +0,0 @@ -{ - "data":{ - "type":"mower", - "id":"feed-beef", - "attributes":{ - "system":{ - "name":"AM430X NERA", - "model":"Husqvarna Automower® 430X NERA", - "serialNumber":1234567 - }, - "battery":{ - "batteryPercent":83 - }, - "capabilities":{ - "headlights":true, - "workAreas":true, - "position":true, - "canConfirmError":true, - "stayOutZones":true - }, - "mower":{ - "mode":"MAIN_AREA", - - MAIN_AREA - - SECONDARY_AREA - - HOME - - DEMO - - UNKNOWN - - POI - "activity":"NOT_APPLICABLE", - - UNKNOWN - - NOT_APPLICABLE - - MOWING - - GOING_HOME - - CHARGING - - LEAVING - - PARKED_IN_CS - - STOPPED_IN_GARDEN - "inactiveReason":"NONE", - - 'NONE' - - 'PLANNING' - - 'SEARCHING_FOR_SATELLITES' - "state":"STOPPED", - "workAreaId":4294967295, - "errorCode":0, - "errorCodeTimestamp":0, - "isErrorConfirmable":false - }, - "calendar":{ - "tasks":[ - { - "start":420, - "duration":360, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":0 - }, - { - "start":0, - "duration":1440, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":20988 - }, - { - "start":0, - "duration":1440, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":2343 - }, - { - "start":0, - "duration":1440, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":20463 - }, - { - "start":0, - "duration":1440, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":26716 - }, - { - "start":450, - "duration":330, - "monday":true, - "tuesday":true, - "wednesday":true, - "thursday":true, - "friday":true, - "saturday":true, - "sunday":true, - "workAreaId":17746 - } - ] - }, - "planner":{ - "nextStartTimestamp":1728804600000, - "override":{ - "action":"NOT_ACTIVE" - - NOT_ACTIVE - - FORCE_PARK - - FORCE_MOW - }, - "restrictedReason":"WEEK_SCHEDULE" - - NONE - - WEEK_SCHEDULE - - PARK_OVERRIDE - - SENSOR - - DAILY_LIMIT - - FOTA - - FROST - - ALL_WORK_AREAS_COMPLETED - - EXTERNAL - }, - "metadata":{ - "connected":false, - "statusTimestamp":1728733281198 - }, - "workAreas":[ - { - "workAreaId":0, - "name":"", - "cuttingHeight":100, - "enabled":false - }, - { - "workAreaId":18569, - "name":"Kompost", - "cuttingHeight":100, - "enabled":false - }, - { - "workAreaId":17746, - "name":"Arbeitsbereich HERBST", - "cuttingHeight":88, - "enabled":true - }, - { - "workAreaId":20988, - "name":"Einfahrt", - "cuttingHeight":100, - "enabled":false - }, - { - "workAreaId":2343, - "name":"Garten N", - "cuttingHeight":100, - "enabled":false - }, - { - "workAreaId":20463, - "name":"Garten S", - "cuttingHeight":100, - "enabled":false - }, - { - "workAreaId":26716, - "name":"Fleckl", - "cuttingHeight":100, - "enabled":false - } - ], - "positions":[ - { - "latitude":49.4885543, - "longitude":11.4403704 - }, - { - "latitude":49.4886409, - "longitude":11.4402991 - }, - { - "latitude":49.4886345, - "longitude":11.4403477 - }, - { - "latitude":49.4886191, - "longitude":11.4404409 - }, - { - "latitude":49.4886154, - "longitude":11.4404571 - }, - { - "latitude":49.4885625, - "longitude":11.4404572 - }, - { - "latitude":49.4885106, - "longitude":11.44044 - }, - { - "latitude":49.4885359, - "longitude":11.440381 - }, - { - "latitude":49.4886038, - "longitude":11.4403824 - }, - { - "latitude":49.4885955, - "longitude":11.4403705 - }, - { - "latitude":49.4885553, - "longitude":11.4403891 - }, - { - "latitude":49.4885065, - "longitude":11.4404362 - }, - { - "latitude":49.4885752, - "longitude":11.4404618 - }, - { - "latitude":49.488634, - "longitude":11.4404194 - }, - { - "latitude":49.4886403, - "longitude":11.4403184 - }, - { - "latitude":49.4886432, - "longitude":11.4403063 - }, - { - "latitude":49.4886393, - "longitude":11.4403557 - }, - { - "latitude":49.4886167, - "longitude":11.4404489 - }, - { - "latitude":49.4885441, - "longitude":11.4404501 - }, - { - "latitude":49.4885495, - "longitude":11.4404615 - }, - { - "latitude":49.4886104, - "longitude":11.4404536 - }, - { - "latitude":49.488601, - "longitude":11.4404616 - }, - { - "latitude":49.4885229, - "longitude":11.4404567 - }, - { - "latitude":49.4885221, - "longitude":11.4404052 - }, - { - "latitude":49.4885585, - "longitude":11.4403508 - }, - { - "latitude":49.4885367, - "longitude":11.4402899 - }, - { - "latitude":49.4886131, - "longitude":11.4403878 - }, - { - "latitude":49.4885686, - "longitude":11.4404124 - }, - { - "latitude":49.4885122, - "longitude":11.4403419 - }, - { - "latitude":49.4885847, - "longitude":11.4403082 - }, - { - "latitude":49.4885781, - "longitude":11.440312 - }, - { - "latitude":49.488555, - "longitude":11.4403824 - }, - { - "latitude":49.488523, - "longitude":11.4403905 - }, - { - "latitude":49.488579, - "longitude":11.4403191 - }, - { - "latitude":49.4886067, - "longitude":11.4403727 - }, - { - "latitude":49.4885113, - "longitude":11.4403436 - }, - { - "latitude":49.4885584, - "longitude":11.4403292 - }, - { - "latitude":49.488593, - "longitude":11.4404123 - }, - { - "latitude":49.488536, - "longitude":11.4403742 - }, - { - "latitude":49.4885764, - "longitude":11.4403652 - }, - { - "latitude":49.4885773, - "longitude":11.4404133 - }, - { - "latitude":49.4885379, - "longitude":11.4402983 - }, - { - "latitude":49.4885529, - "longitude":11.4404118 - }, - { - "latitude":49.488566, - "longitude":11.4402938 - }, - { - "latitude":49.488538, - "longitude":11.4403783 - }, - { - "latitude":49.4886072, - "longitude":11.4403713 - }, - { - "latitude":49.4885509, - "longitude":11.4403495 - }, - { - "latitude":49.488525, - "longitude":11.4403647 - }, - { - "latitude":49.4885137, - "longitude":11.4403337 - }, - { - "latitude":49.488588, - "longitude":11.4403226 - } - ], - "settings":{ - "cuttingHeight":8, - "headlight":{ - "mode":"EVENING_AND_NIGHT" - } - }, - "statistics":{ - "cuttingBladeUsageTime":830796, - "numberOfChargingCycles":98, - "numberOfCollisions":6940, - "totalChargingTime":224782, - "totalCuttingTime":830796, - "totalDriveDistance":369223, - "totalRunningTime":900545, - "totalSearchingTime":51789 - }, - "stayOutZones":{ - "zones":[ - { - "id":"ad18bfae-33ae-45f2-9dc5-0b7c1b4b4fd2", - "name":"Ausgeschlossener Bereich", - "enabled":true - } - ], - "dirty":false - } - } - } - } - From 320f6e5547f9ec7801bcd3a949da1bd66272ec1b Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Tue, 22 Oct 2024 22:18:48 +0200 Subject: [PATCH 24/35] own review + findings Signed-off-by: Michael Weger --- .../internal/actions/AutomowerActions.java | 13 ++++++++++-- .../internal/bridge/AutomowerBridge.java | 11 +++++----- .../automowerconnect/AutomowerConnectApi.java | 2 +- .../internal/things/AutomowerHandler.java | 21 ++++++++++++++----- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index 6b9388b03aefc..fa0ad55df1050 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -201,8 +201,17 @@ public static void setStayOutZone(ThingActions actions, String zoneId, boolean e } @RuleAction(label = "@text/action-set-calendartask-label", description = "@text/action-set-calendartask-desc") - public void setCalendarTask(Long workAreaId, short[] start, short[] duration, boolean[] monday, boolean[] tuesday, - boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, boolean[] sunday) { + public void setCalendarTask( + @ActionInput(name = "workarea-id", label = "@text/action-input-workarea-id-label", description = "@text/action-input-workarea-id-desc") Long workAreaId, + @ActionInput(name = "start", label = "@text/action-input-start-label", description = "@text/action-input-start-desc") short[] start, + @ActionInput(name = "duration", label = "@text/action-input-duration-label", description = "@text/action-input-duration-desc") short[] duration, + @ActionInput(name = "monday", label = "@text/action-input-monday-label", description = "@text/action-input-monday-desc") boolean[] monday, + @ActionInput(name = "tuesday", label = "@text/action-input-tuesday-label", description = "@text/action-input-tuesday-desc") boolean[] tuesday, + @ActionInput(name = "wednesday", label = "@text/action-input-wednesday-label", description = "@text/action-input-wednesday-desc") boolean[] wednesday, + @ActionInput(name = "thursday", label = "@text/action-input-thursday-label", description = "@text/action-input-thursday-desc") boolean[] thursday, + @ActionInput(name = "friday", label = "@text/action-input-friday-label", description = "@text/action-input-friday-desc") boolean[] friday, + @ActionInput(name = "saturday", label = "@text/action-input-saturday-label", description = "@text/action-input-saturday-desc") boolean[] saturday, + @ActionInput(name = "sunday", label = "@text/action-input-sunday-label", description = "@text/action-input-sunday-desc") boolean[] sunday) { AutomowerHandler automowerHandler = handler; if (automowerHandler == null) { logger.warn("Automower Action service ThingHandler is null!"); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 2d2201e66d0cc..311d69e153cd9 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -122,15 +122,16 @@ public void sendAutomowerCommand(String id, AutomowerCommand command, long comma * Sends a calendarTask to the automower * * @param id The id of the mower + * @param hasWorkAreas Work area capability of the mower * @param workAreaId The Id of the work area this calendar belongs to (or null, if there is no work area support) - * @param calendarTask The calendar that should be sent. It is using the same json structure (start, duration, ...) + * @param calendarTasks The calendar that should be sent. It is using the same json structure (start, duration, ...) * as provided when reading the channel * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ public void sendAutomowerCalendarTask(String id, boolean hasWorkAreas, Long workAreaId, - List calendarTaskArray) throws AutomowerCommunicationException { + List calendarTasks) throws AutomowerCommunicationException { Calendar calendar = new Calendar(); - calendar.setTasks(calendarTaskArray); + calendar.setTasks(calendarTasks); MowerCalendar mowerCalendar = new MowerCalendar(); mowerCalendar.setType("calendar"); @@ -177,7 +178,7 @@ public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationEx * * @param id The id of the mower * @param zoneId The id of the stay out zone - * @param zoneRequest The new zone status + * @param zoneAttributes The new zone status * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneAttributes zoneAttributes) @@ -197,7 +198,7 @@ public void sendAutomowerStayOutZone(String id, String zoneId, MowerStayOutZoneA * * @param id The id of the mower * @param workAreaId The id of the work area - * @param workAreaRequest The new work area status + * @param workAreaAttributes The new work area status * @throws AutomowerCommunicationException In case the query cannot be executed successfully */ public void sendAutomowerWorkArea(String id, long workAreaId, MowerWorkAreaAttributes workAreaAttributes) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index dfc92a1623812..3d837c45e26ba 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -84,7 +84,7 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ public void sendCalendar(String appKey, String token, String id, boolean hasWorkAreas, Long workAreaId, MowerCalendardRequest calendar) throws AutomowerCommunicationException { String url; - if (hasWorkAreas) { + if (hasWorkAreas && (workAreaId != null)) { url = getBaseUrl() + "/mowers/" + id + "/workAreas/" + workAreaId + "/calendar"; } else { url = getBaseUrl() + "/mowers/" + id + "/calendar"; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index c79725b160ce5..42ef14b2dc15c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -700,11 +700,14 @@ private void updateChannelState(@Nullable Mower mower) { Long workAreaId = mower.getAttributes().getMower().getWorkAreaId(); if (workAreaId != null) { - updateState(CHANNEL_STATUS_WORK_AREA_ID, - new DecimalType(mower.getAttributes().getMower().getWorkAreaId())); + updateState(CHANNEL_STATUS_WORK_AREA_ID, new DecimalType(workAreaId)); WorkArea workArea = getWorkAreaById(mower, workAreaId); if ((workArea != null) && (workArea.getName() != null)) { - updateState(CHANNEL_STATUS_WORK_AREA, new StringType(workArea.getName())); + if ((workAreaId.equals(0L)) && workArea.getName().isBlank()) { + updateState(CHANNEL_STATUS_WORK_AREA, new StringType("main area")); + } else { + updateState(CHANNEL_STATUS_WORK_AREA, new StringType(workArea.getName())); + } } else { updateState(CHANNEL_STATUS_WORK_AREA, UnDefType.NULL); } @@ -815,7 +818,11 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_CALENDARTASKS.get(j++), new DecimalType(calendarTasks.get(i).getWorkAreaId())); WorkArea workArea = getWorkAreaById(mower, calendarTasks.get(i).getWorkAreaId()); if (workArea != null) { - updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType(workArea.getName())); + if ((calendarTasks.get(i).getWorkAreaId().equals(0L)) && workArea.getName().isBlank()) { + updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType("main area")); + } else { + updateState(CHANNEL_CALENDARTASKS.get(j++), new StringType(workArea.getName())); + } } else { updateState(CHANNEL_CALENDARTASKS.get(j++), UnDefType.NULL); } @@ -843,7 +850,11 @@ private void updateChannelState(@Nullable Mower mower) { for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { WorkArea workArea = workAreas.get(i); updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workArea.getWorkAreaId())); - updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); + if ((workArea.getWorkAreaId() == 0L) && workArea.getName().isBlank()) { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType("main area")); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); + } updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workArea.getCuttingHeight(), Units.PERCENT)); updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workArea.isEnabled())); if (workArea.getProgress() != null) { From 159b828e7a6b6f94036987463de22c6f764832dd Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 9 Nov 2024 15:46:40 +0100 Subject: [PATCH 25/35] robustness improvements Signed-off-by: Michael Weger --- .../internal/things/AutomowerHandler.java | 105 +++++++++++------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 42ef14b2dc15c..14131a742400f 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -261,9 +261,9 @@ private boolean isValidResult(@Nullable Mower mower) { && (mower.getAttributes().getCalendar() != null) && (mower.getAttributes().getCalendar().getTasks() != null) && (mower.getAttributes().getCapabilities() != null) && (mower.getAttributes().getMower() != null) - && (mower.getAttributes().getPlanner() != null) && (mower.getAttributes().getPositions() != null) - && (mower.getAttributes().getSettings() != null) && (mower.getAttributes().getStatistics() != null) - && (mower.getAttributes().getSystem() != null)); + && (mower.getAttributes().getPlanner() != null) + && (mower.getAttributes().getPlanner().getOverride() != null) + && (mower.getAttributes().getSettings() != null) && (mower.getAttributes().getStatistics() != null)); } private boolean isConnected(@Nullable Mower mower) { @@ -743,15 +743,25 @@ private void updateChannelState(@Nullable Mower mower) { } updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, - new StringType(mower.getAttributes().getPlanner().getRestrictedReason().name())); + RestrictedReason restrictedReason = mower.getAttributes().getPlanner().getRestrictedReason(); + if (restrictedReason != null) { + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(restrictedReason.name())); + } else { + updateState(CHANNEL_PLANNER_RESTRICTED_REASON, UnDefType.NULL); + } + updateState(CHANNEL_PLANNER_EXTERNAL_REASON, new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); updateState(CHANNEL_SETTING_CUTTING_HEIGHT, new DecimalType(mower.getAttributes().getSettings().getCuttingHeight())); - updateState(CHANNEL_SETTING_HEADLIGHT_MODE, - new StringType(mower.getAttributes().getSettings().getHeadlight().getHeadlightMode().name())); + + Headlight headlight = mower.getAttributes().getSettings().getHeadlight(); + if (headlight != null) { + updateState(CHANNEL_SETTING_HEADLIGHT_MODE, new StringType(headlight.getHeadlightMode().name())); + } else { + updateState(CHANNEL_SETTING_HEADLIGHT_MODE, UnDefType.NULL); + } updateState(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND)); @@ -786,13 +796,20 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, new QuantityType<>(0, Units.PERCENT)); } - updateState(LAST_POSITION, - new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), - new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); - List positions = mower.getAttributes().getPositions(); - for (int i = 0; i < positions.size(); i++) { - updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), - new DecimalType(positions.get(i).getLongitude()))); + if (mower.getAttributes().getLastPosition() != null) { + updateState(LAST_POSITION, + new PointType(new DecimalType(mower.getAttributes().getLastPosition().getLatitude()), + new DecimalType(mower.getAttributes().getLastPosition().getLongitude()))); + List positions = mower.getAttributes().getPositions(); + for (int i = 0; i < positions.size(); i++) { + updateState(CHANNEL_POSITIONS.get(i), new PointType(new DecimalType(positions.get(i).getLatitude()), + new DecimalType(positions.get(i).getLongitude()))); + } + } else { + updateState(LAST_POSITION, UnDefType.NULL); + for (int i = 0; i < CHANNEL_POSITIONS.size(); i++) { + updateState(CHANNEL_POSITIONS.get(i), UnDefType.NULL); + } } updateState(CHANNEL_STAYOUTZONES_DIRTY, OnOffType.from(mower.getAttributes().getStayOutZones().isDirty())); @@ -833,41 +850,49 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_CALENDARTASKS.get(j), UnDefType.NULL); } - List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); j = 0; - for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { - updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); - updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); - updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled())); + if (mower.getAttributes().getStayOutZones() != null) { + List stayOutZones = mower.getAttributes().getStayOutZones().getZones(); + if (stayOutZones != null) { + for (int i = 0; i < stayOutZones.size() && j < CHANNEL_STAYOUTZONES.size(); i++) { + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getId())); + updateState(CHANNEL_STAYOUTZONES.get(j++), new StringType(stayOutZones.get(i).getName())); + updateState(CHANNEL_STAYOUTZONES.get(j++), OnOffType.from(stayOutZones.get(i).isEnabled())); + } + } } // clear remaining channels for (; j < CHANNEL_STAYOUTZONES.size(); j++) { updateState(CHANNEL_STAYOUTZONES.get(j), UnDefType.NULL); } - List workAreas = mower.getAttributes().getWorkAreas(); j = 0; - for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { - WorkArea workArea = workAreas.get(i); - updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workArea.getWorkAreaId())); - if ((workArea.getWorkAreaId() == 0L) && workArea.getName().isBlank()) { - updateState(CHANNEL_WORKAREAS.get(j++), new StringType("main area")); - } else { - updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); - } - updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workArea.getCuttingHeight(), Units.PERCENT)); - updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workArea.isEnabled())); - if (workArea.getProgress() != null) { - updateState(CHANNEL_WORKAREAS.get(j++), new QuantityType<>(workArea.getProgress(), Units.PERCENT)); - } else { - updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); - } - - if ((workArea.getLastTimeCompleted() != null) && (workArea.getLastTimeCompleted() != 0)) { + List workAreas = mower.getAttributes().getWorkAreas(); + if (workAreas != null) { + for (int i = 0; i < workAreas.size() && j < CHANNEL_WORKAREAS.size(); i++) { + WorkArea workArea = workAreas.get(i); + updateState(CHANNEL_WORKAREAS.get(j++), new DecimalType(workArea.getWorkAreaId())); + if ((workArea.getWorkAreaId() == 0L) && workArea.getName().isBlank()) { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType("main area")); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), new StringType(workArea.getName())); + } updateState(CHANNEL_WORKAREAS.get(j++), - new DateTimeType(toZonedDateTime(workArea.getLastTimeCompleted(), mowerZoneId))); - } else { - updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + new QuantityType<>(workArea.getCuttingHeight(), Units.PERCENT)); + updateState(CHANNEL_WORKAREAS.get(j++), OnOffType.from(workArea.isEnabled())); + if (workArea.getProgress() != null) { + updateState(CHANNEL_WORKAREAS.get(j++), + new QuantityType<>(workArea.getProgress(), Units.PERCENT)); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } + + if ((workArea.getLastTimeCompleted() != null) && (workArea.getLastTimeCompleted() != 0)) { + updateState(CHANNEL_WORKAREAS.get(j++), + new DateTimeType(toZonedDateTime(workArea.getLastTimeCompleted(), mowerZoneId))); + } else { + updateState(CHANNEL_WORKAREAS.get(j++), UnDefType.NULL); + } } } // clear remaining channels From 9607dad141b663b884293efc4f62c4311b111455 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 10 Nov 2024 19:35:46 +0100 Subject: [PATCH 26/35] updated README.md Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 214 ++++++++++-------- .../resources/OH-INF/thing/thing-types.xml | 12 +- 2 files changed, 122 insertions(+), 104 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index a9f14b184edeb..5463315dc4502 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -1,26 +1,26 @@ # Automower Binding This is the binding for [Husqvarna Automower a robotic lawn mowers](https://www.husqvarna.com/uk/products/robotic-lawn-mowers/). -This binding allows you to integrate, view and control Automower lawn mowers in the openHAB environment. +This binding allows you to integrate, view and control Automower® lawn mowers in the openHAB environment. ## Supported Things -`bridge:` The bridge needs to be configured with credentials and an application key that allows communicating with the Automower Connect API +`bridge:` The bridge needs to be configured with credentials and an application key that allows communicating with the Automower® Connect API -`automower:` A single Husqvarna Automower robot +`automower:` A single Husqvarna Automower® robot -All Husqvarna Automower models with "Automower Connect" should be supported. It was tested only with a Husqvarna Automower 430X and 450X. +All Husqvarna Automower® models with "Automower® Connect" should be supported. It was tested with a Husqvarna Automower® 430X, 450X and 430X NERA. ## Discovery -Once the bridge is created and configured, openHAB will automatically discover all Automowers registered on your account. +Once the bridge is created and configured, openHAB will automatically discover all Automower® registered on your account. ## Thing Configuration `bridge:` -- appKey (mandatory): The Application Key is required to communicate with the Automower Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). This application also needs to be connected to the ["Authentication API" and the "Automower Connect API"](https://developer.husqvarnagroup.cloud/docs/getting-started) -- appSecret (mandatory): The Application Secret is required to communicate with the Automower Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). +- appKey (mandatory): The Application Key is required to communicate with the Automower® Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). This application also needs to be connected to the ["Authentication API" and the "Automower® Connect API"](https://developer.husqvarnagroup.cloud/docs/getting-started) +- appSecret (mandatory): The Application Secret is required to communicate with the Automower® Connect API. It can be obtained by registering an Application on [the Husqvarna Website](https://developer.husqvarnagroup.cloud/). - pollingInterval (optional): How often the bridge state should be queried in seconds. Default is 1h (3600s) Keep in mind that the status of the bridge should not be queried too often. @@ -29,113 +29,133 @@ With the default value of 1h this would mean ~720 requests per month for the bri `automower:` -- mowerId (mandatory): The Id of an automower as used by the Automower Connect Api to identify a mower. This is automatically filled when the thing is discovered -- pollingInterval (optional): How often the current automower state should be polled in seconds. Default is 10min (600s) +- mowerId (mandatory): The Id of an Automower® as used by the Automower® Connect Api to identify a mower. This is automatically filled when the thing is discovered +- pollingInterval (optional): How often the current Automower® state should be polled in seconds. Default is 10min (600s) -Keep in mind that the status of the Automowers should not be queried too often. +Keep in mind that the status of the Automower® should not be queried too often. According to the Husqvarna documentation, no more than 10000 requests per month and application key are allowed. -With the default value of 10min this would mean ~4300 requests per month per single Automower +With the default value of 10min this would mean ~4300 requests per month per single Automower® ## Channels ### Status Channels -| channel | type | access mode | description | -|-------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | -| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | -| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | -| last-update | DateTime | R | The time when the automower updated its states | -| battery | Number | R | The battery state of charge in percent | -| error-code | Number | R | The current error code | -| error-timestamp | DateTime | R | The timestamp when the current error occurred | -| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL. | -| planner-override-action | String | R | The action that overrides current planner operation. | -| calendar-tasks | String | R | The JSON with the information about Automower planner. | - -### Command Channels - -| channel | type | access mode | description | -|-----------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| start | Number | W | Starts the automower for a duration | -| resume_schedule | Switch | W | Resumes the Automower schedule | -| pause | Switch | W | Pause the Automower | -| park | Number | W | Park the Automower for a duration | -| park_until_next_schedule | Switch | W | Park the Automower until next schedule | -| park_until_further_notice | Switch | W | Park the Automower until further notice. | +| channel | type | access mode | description | +|------------|----------|-------------|--------------| +| name | String | R | The name of the Automower® | +| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | +| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | +| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | +| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | +| work-area-id | Number | R | Id of the active work area | +| work-area | String | R | Name of the active work area | +| last-update | DateTime | R | The time when the Automower® updated its states | +| battery | Number:Dimensionless | R | The battery state of charge in percent | +| error-code | Number | R | The current error code | +| error-timestamp | DateTime | R | The timestamp when the current error occurred | +| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | +| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | +| planner-override-action | String | R | The action that overrides current planner operation | +| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | +| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | +| setting-cutting-height | Number | R | Prescaled cutting height, Range: 1-9 | +| setting-headlight-mode | String | R | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | +| stat-cutting-blade-usage-time | Number:Time | R | The time since the last reset of the cutting blade usage counter | +| stat-number-of-charging-cycles | Number | R | Number of charging cycles | +| stat-number-of-collisions | Number | R | The total number of collisions | +| stat-total-charging-time | Number:Time | R | Total charging time | +| stat-total-cutting-time | Number:Time | R | Total Cutting Time | +| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | +| stat-total-drive-distance | Number:Length | R | Total driven distance | +| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | +| stat-total-searching-time | Number:Length | R | The total searching time | +| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | + +### Calendar Tasks Channels + +These channels hold the different Calendar Task configurations. Right now a maximum of 10 Calendar Tasks are supported by the binding. + +| channel | type | access mode | description | +|------------|----------|-------------|--------------| +| calendartasks\-start | Number:Time | R/W | Start time relative to midnight | +| calendartasks\-duration | Number:Time | R/W | Duration time | +| calendartasks\-monday | Switch | R/W | Enabled on Mondays | +| calendartasks\-tuesday | Switch | R/W | Enabled on Tuesdays | +| calendartasks\-wednesday | Switch | R/W | Enabled on Wednesdays | +| calendartasks\-thursday | Switch | R/W | Enabled on Thursdays | +| calendartasks\-friday | Switch | R/W | Enabled on Fridays | +| calendartasks\-saturday | Switch | R/W | Enabled on Saturdays | +| calendartasks\-sunday | Switch | R/W | Enabled on Sundays | +| calendartasks\-workAreaId | Number | R | Work Area Id mapped to this calendar | +| calendartasks\-workArea | String | R | Name of the Work Area mapped to this calendar | + +\ ... 01-10 ### Position Channels -These channels hold the last 50 GPS positions recorded by the Automower, thus describing the path it followed. +These channels hold the last 50 GPS positions recorded by the Automower®, thus describing the path it followed. Position 01 is the latest recorded position, the other positions are pushed back, thus removing the previous position 50 from the list because it is replaced by the previous position 49. Channel `last-position` is always identical with channel `position01` and thus provides more convenient access if only the latest GPS information is required by the user. -| channel | type | access mode | description | -|------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| position01 | Location | R | GPS Position 01 | -| position02 | Location | R | GPS Position 02 | -| position03 | Location | R | GPS Position 03 | -| position04 | Location | R | GPS Position 04 | -| position05 | Location | R | GPS Position 05 | -| position06 | Location | R | GPS Position 06 | -| position07 | Location | R | GPS Position 07 | -| position08 | Location | R | GPS Position 08 | -| position09 | Location | R | GPS Position 09 | -| position10 | Location | R | GPS Position 10 | -| position11 | Location | R | GPS Position 11 | -| position12 | Location | R | GPS Position 12 | -| position13 | Location | R | GPS Position 13 | -| position14 | Location | R | GPS Position 14 | -| position15 | Location | R | GPS Position 15 | -| position16 | Location | R | GPS Position 16 | -| position17 | Location | R | GPS Position 17 | -| position18 | Location | R | GPS Position 18 | -| position19 | Location | R | GPS Position 19 | -| position20 | Location | R | GPS Position 20 | -| position21 | Location | R | GPS Position 21 | -| position22 | Location | R | GPS Position 22 | -| position23 | Location | R | GPS Position 23 | -| position24 | Location | R | GPS Position 24 | -| position25 | Location | R | GPS Position 25 | -| position26 | Location | R | GPS Position 26 | -| position27 | Location | R | GPS Position 27 | -| position28 | Location | R | GPS Position 28 | -| position29 | Location | R | GPS Position 29 | -| position30 | Location | R | GPS Position 30 | -| position31 | Location | R | GPS Position 31 | -| position32 | Location | R | GPS Position 32 | -| position33 | Location | R | GPS Position 33 | -| position34 | Location | R | GPS Position 34 | -| position35 | Location | R | GPS Position 35 | -| position36 | Location | R | GPS Position 36 | -| position37 | Location | R | GPS Position 37 | -| position38 | Location | R | GPS Position 38 | -| position39 | Location | R | GPS Position 39 | -| position40 | Location | R | GPS Position 40 | -| position41 | Location | R | GPS Position 41 | -| position42 | Location | R | GPS Position 42 | -| position43 | Location | R | GPS Position 43 | -| position44 | Location | R | GPS Position 44 | -| position45 | Location | R | GPS Position 45 | -| position46 | Location | R | GPS Position 46 | -| position47 | Location | R | GPS Position 47 | -| position48 | Location | R | GPS Position 48 | -| position49 | Location | R | GPS Position 49 | -| position50 | Location | R | GPS Position 50 | -| last-position | Location | R | Last GPS Position (identical with positions#position01) | +| channel | type | access mode | description | advanced | +|------------|----------|-------------|--------------|----------| +| last-position | Location | R | Last GPS Position (identical with positions#position01) | false | +| position\ | Location | R | GPS Position \ | true | + +\ ... 01-50 + +### Stayout Zones Channels + +These channels hold the different Stayout Zone configurations. Right now a maximum of 10 Stayout Zones are supported by the binding. + +| channel | type | access mode | description | advanced | +|------------|----------|-------------|--------------|----------| +| dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | +| zone\-id | Number | R | Id of the Stayout zone | false | +| zone\-name | String | R | The name of the Stayout zone | false | +| zone\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | false | + +\ ... 01-10 + +### Work Area Channels + +These channels hold the different Work Area configurations. Right now a maximum of 10 Work Areas are supported by the binding. + +| channel | type | access mode | description | advanced | +|------------|----------|-------------|--------------|----------| +| workarea\-id | Number | R | Id of the Work Area | false | +| workarea\-name | String | R | Name of the work area | false | +| workarea\-cutting-height | Number:Dimensionless | R/W | Cutting height in percent. 0-100 | false | +| workarea\-enabled | Switch | R/W | If the work area is enabled or disabled | false | +| workarea\-progress | Number | R | The progress on a work area. EPOS mowers and systematic mowing work areas only | true | +| workarea\-last-time-completed | DateTime | R | Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only | true | + +\ ... 01-10 + +### Command Channels + +| channel | type | access mode | description | +|-----------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| start | Number | W | Starts the Automower® for a duration | +| resume_schedule | Switch | W | Resumes the Automower® schedule | +| pause | Switch | W | Pause the Automower® | +| park | Number | W | Park the Automower® for a duration | +| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | +| park_until_further_notice | Switch | W | Park the Automower® until further notice. | +| confirm_error | Switch | W | Confirm current non fatal error | ## Actions The following actions are available for `automower`things: -| action name | arguments | description | -|------------------------|----------------|------------------------------------------------------------------------------------------------| -| start | duration (int) | Starts the automower for the given duration (minutes), overriding the schedule | -| pause | - | Pauses the automower wherever it is currently located | -| parkUntilNextSchedule | - | Parks the automower, fully charges it and starts afterwards according to the schedule | -| parkUntilFurtherNotice | - | Parks the automower until it is started again by the start action or the schedule gets resumed | -| park | duration (int) | Parks the automower for the given duration (minutes), overriding the schedule | -| resumeSchedule | - | Resumes the schedule for the automower | +| action name | arguments | description | +|------------------------|----------------|-------------------------------------------------------------------------------------------------| +| start | duration (int) | Starts the Automower® for the given duration (minutes), overriding the schedule | +| pause | - | Pauses the Automower® wherever it is currently located | +| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | +| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | +| park | duration (int) | Parks the Automower® for the given duration (minutes), overriding the schedule | +| resumeSchedule | - | Resumes the schedule for the Automower® | ## Full Example @@ -194,7 +214,7 @@ sitemap demo label="Automower" ### automower.rule -Example rule that triggers an automower action +Example rule that triggers an Automower® action ```java rule "AutomowerParkUntilFurtherNotice" diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index ca5d2f47c4eef..32ab6cb6a4d14 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -532,7 +532,7 @@ String - The channel providing an reason that restrics current planner operation. + The channel providing a reason that restrics current planner operation. @@ -707,7 +707,7 @@ - + Switch If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable @@ -733,7 +733,6 @@ Switch If the Stayout zone is enabled, the Automower® will not access the zone - @@ -755,24 +754,23 @@ Number:Dimensionless Cutting height in percent. 0-100 - + Switch If the work area is enabled or disabled - - + Number:Dimensionless The progress on a work area. EPOS mowers and systematic mowing work areas only. - + DateTime Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. From cc07d5e0016e81302a82d8d29c05dda453172685 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 10 Nov 2024 19:57:09 +0100 Subject: [PATCH 27/35] added actions to README.md Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 5463315dc4502..74b447c9bc949 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -146,16 +146,21 @@ These channels hold the different Work Area configurations. Right now a maximum ## Actions -The following actions are available for `automower`things: - -| action name | arguments | description | -|------------------------|----------------|-------------------------------------------------------------------------------------------------| -| start | duration (int) | Starts the Automower® for the given duration (minutes), overriding the schedule | -| pause | - | Pauses the Automower® wherever it is currently located | -| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | -| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | -| park | duration (int) | Parks the Automower® for the given duration (minutes), overriding the schedule | -| resumeSchedule | - | Resumes the schedule for the Automower® | +The following actions are available for `automower` things: + +| action name | arguments | description | +|------------------------|------------------|-------------------------------------------------------------------------------------------------| +| start | `duration (int)` | Starts the Automower® for the given duration (minutes), overriding the schedule | +| pause | - | Pauses the Automower® wherever it is currently located | +| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | +| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | +| park | `duration (int)` | Parks the Automower® for the given duration (minutes), overriding the schedule | +| resumeSchedule | - | Resumes the schedule for the Automower® | +| confirmError | - | Confirm current non fatal error | +| setSettings | `byte cuttingHeight, String headlightMode` | Update Automower® settings | +| setWorkArea | `long workAreaId, boolean enable, byte cuttingHeight` | Update work area settings | +| setStayOutZone | `String zoneId, boolean enable` | Enable or disable stay-out zone | +| setCalendarTask | `Long workAreaId, short[] start, short[] duration, boolean[] monday, boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, boolean[] sunday` | Update calendar task settings | ## Full Example @@ -171,25 +176,25 @@ Bridge automower:bridge:mybridge [ appKey="", user ### automower.items ```java -String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:mode" } -String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:activity" } -String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:state" } -DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:last-update" } -Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:battery" } -Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:error-code" } -DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:error-timestamp" } -String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:planner-override-action" } -DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:planner-next-start" } -String Automower_Calendar_Tasks "Planned Tasks [%s]" { channel="automower:automower:mybridge:myAutomower:calendar-tasks" } - -Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:start" } -Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:resume_schedule" } -Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:pause" } -Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:park" } -Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:park_until_next_schedule" } -Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:park_until_further_notice" } - -Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:last-position" } +String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:mode" } +String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:activity" } +String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:state" } +DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:last-update" } +Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:battery" } +Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:error-code" } +DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:error-timestamp" } +String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:planner-override-action" } +DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:planner-next-start" } +String Automower_Calendar_Tasks "Planned Tasks [%s]" { channel="automower:automower:mybridge:myAutomower:calendar-tasks" } + +Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:start" } +Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:resume_schedule" } +Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:pause" } +Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:park" } +Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:park_until_next_schedule" } +Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:park_until_further_notice" } + +Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:last-position" } ``` ### automower.sitemap From c2269c50679423f55d952382380d72504da07d75 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sun, 10 Nov 2024 20:04:33 +0100 Subject: [PATCH 28/35] README.md Signed-off-by: Michael Weger --- bundles/org.openhab.binding.automower/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 74b447c9bc949..5c43554d87145 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -1,6 +1,6 @@ # Automower Binding -This is the binding for [Husqvarna Automower a robotic lawn mowers](https://www.husqvarna.com/uk/products/robotic-lawn-mowers/). +This is the binding for [Husqvarna Automower® robotic lawn mowers](https://www.husqvarna.com/uk/products/robotic-lawn-mowers/). This binding allows you to integrate, view and control Automower® lawn mowers in the openHAB environment. ## Supported Things From ab0fe974b87e3314d0f110c5c93ae39e63a55d1e Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 11 Nov 2024 20:47:45 +0100 Subject: [PATCH 29/35] testing, fixes Signed-off-by: Michael Weger --- bundles/org.openhab.binding.automower/README.md | 16 ++++++++-------- .../internal/things/AutomowerHandler.java | 1 + .../main/resources/OH-INF/thing/thing-types.xml | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 5c43554d87145..46300223a2a4f 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -59,7 +59,7 @@ With the default value of 10min this would mean ~4300 requests per month per sin | planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | | planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | | setting-cutting-height | Number | R | Prescaled cutting height, Range: 1-9 | -| setting-headlight-mode | String | R | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | +| setting-headlight-mode | String | R | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | | stat-cutting-blade-usage-time | Number:Time | R | The time since the last reset of the cutting blade usage counter | | stat-number-of-charging-cycles | Number | R | Number of charging cycles | | stat-number-of-collisions | Number | R | The total number of collisions | @@ -110,8 +110,8 @@ These channels hold the different Stayout Zone configurations. Right now a maxim | channel | type | access mode | description | advanced | |------------|----------|-------------|--------------|----------| -| dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | -| zone\-id | Number | R | Id of the Stayout zone | false | +| dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | +| zone\-id | String | R | Id of the Stayout zone | false | | zone\-name | String | R | The name of the Stayout zone | false | | zone\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | false | @@ -141,7 +141,7 @@ These channels hold the different Work Area configurations. Right now a maximum | pause | Switch | W | Pause the Automower® | | park | Number | W | Park the Automower® for a duration | | park_until_next_schedule | Switch | W | Park the Automower® until next schedule | -| park_until_further_notice | Switch | W | Park the Automower® until further notice. | +| park_until_further_notice | Switch | W | Park the Automower® until further notice | | confirm_error | Switch | W | Confirm current non fatal error | ## Actions @@ -157,10 +157,10 @@ The following actions are available for `automower` things: | park | `duration (int)` | Parks the Automower® for the given duration (minutes), overriding the schedule | | resumeSchedule | - | Resumes the schedule for the Automower® | | confirmError | - | Confirm current non fatal error | -| setSettings | `byte cuttingHeight, String headlightMode` | Update Automower® settings | -| setWorkArea | `long workAreaId, boolean enable, byte cuttingHeight` | Update work area settings | -| setStayOutZone | `String zoneId, boolean enable` | Enable or disable stay-out zone | -| setCalendarTask | `Long workAreaId, short[] start, short[] duration, boolean[] monday, boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, boolean[] sunday` | Update calendar task settings | +| setSettings | `byte cuttingHeight`
`String headlightMode` | Update Automower® settings | +| setWorkArea | `long workAreaId`
`boolean enable`
`byte cuttingHeight` | Update work area settings | +| setStayOutZone | `String zoneId`
`boolean enable` | Enable or disable stay-out zone | +| setCalendarTask | `Long workAreaId` (optional, set to `null` if the mower doesn't support work areas)
`short[] start`
`short[] duration`
`boolean[] monday`
`boolean[] tuesday`
`boolean[] wednesday`
`boolean[] thursday`
`boolean[] friday`
`boolean[] saturday`
`boolean[] sunday` | Update calendar task settings | ## Full Example diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 14131a742400f..5fce8ea4a7196 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -621,6 +621,7 @@ public void sendAutomowerSettings(byte cuttingHeight, HeadlightMode headlightMod settings.setCuttingHeight(cuttingHeight); Headlight headlight = new Headlight(); headlight.setHeadlightMode(headlightMode); + settings.setHeadlight(headlight); // update local cache ... mowerState.getAttributes().getSettings().setCuttingHeight(cuttingHeight); diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 32ab6cb6a4d14..09fc1544c7dc8 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -286,7 +286,7 @@ - + From db81ccaf01682170de1ecfe6e649fcfff87962cd Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Wed, 13 Nov 2024 19:46:57 +0100 Subject: [PATCH 30/35] mvn i18n:generate-default-translations Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 56 +++--- .../internal/actions/AutomowerActions.java | 4 +- .../internal/things/AutomowerHandler.java | 2 +- .../OH-INF/i18n/automower.properties | 164 +++++++++++++++++- 4 files changed, 187 insertions(+), 39 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 46300223a2a4f..b2785445f07a6 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -42,34 +42,34 @@ With the default value of 10min this would mean ~4300 requests per month per sin | channel | type | access mode | description | |------------|----------|-------------|--------------| -| name | String | R | The name of the Automower® | -| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | -| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | -| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | -| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | -| work-area-id | Number | R | Id of the active work area | -| work-area | String | R | Name of the active work area | -| last-update | DateTime | R | The time when the Automower® updated its states | -| battery | Number:Dimensionless | R | The battery state of charge in percent | -| error-code | Number | R | The current error code | -| error-timestamp | DateTime | R | The timestamp when the current error occurred | -| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | -| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | -| planner-override-action | String | R | The action that overrides current planner operation | -| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | -| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | -| setting-cutting-height | Number | R | Prescaled cutting height, Range: 1-9 | -| setting-headlight-mode | String | R | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | -| stat-cutting-blade-usage-time | Number:Time | R | The time since the last reset of the cutting blade usage counter | -| stat-number-of-charging-cycles | Number | R | Number of charging cycles | -| stat-number-of-collisions | Number | R | The total number of collisions | -| stat-total-charging-time | Number:Time | R | Total charging time | -| stat-total-cutting-time | Number:Time | R | Total Cutting Time | -| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | -| stat-total-drive-distance | Number:Length | R | Total driven distance | -| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | -| stat-total-searching-time | Number:Length | R | The total searching time | -| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | +| name | String | R | The name of the Automower® | +| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | +| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | +| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | +| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | +| work-area-id | Number | R | Id of the active work area | +| work-area | String | R | Name of the active work area | +| last-update | DateTime | R | The time when the Automower® updated its states | +| battery | Number:Dimensionless | R | The battery state of charge in percent | +| error-code | Number | R | The current error code | +| error-timestamp | DateTime | R | The timestamp when the current error occurred | +| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | +| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | +| planner-override-action | String | R | The action that overrides current planner operation | +| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | +| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | +| setting-cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | +| setting-headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | +| stat-cutting-blade-usage-time | Number:Time | R | The time since the last reset of the cutting blade usage counter | +| stat-number-of-charging-cycles | Number | R | Number of charging cycles | +| stat-number-of-collisions | Number | R | The total number of collisions | +| stat-total-charging-time | Number:Time | R | Total charging time | +| stat-total-cutting-time | Number:Time | R | Total Cutting Time | +| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | +| stat-total-drive-distance | Number:Length | R | Total driven distance | +| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | +| stat-total-searching-time | Number:Length | R | The total searching time | +| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | ### Calendar Tasks Channels diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index fa0ad55df1050..70497a379562e 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -147,7 +147,7 @@ public static void confirmError(ThingActions actions) { ((AutomowerActions) actions).confirmError(); } - @RuleAction(label = "@text/action-set-Settings-label", description = "@text/action-set-Settings-desc") + @RuleAction(label = "@text/action-set-settings-label", description = "@text/action-set-settings-desc") public void setSettings( @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight, @ActionInput(name = "headlight-mode", label = "@text/action-input-headlight-mode-label", description = "@text/action-input-headlight-mode-desc") String headlightMode) { @@ -167,7 +167,7 @@ public static void setSettings(ThingActions actions, byte cuttingHeight, String ((AutomowerActions) actions).setSettings(cuttingHeight, headlightMode); } - @RuleAction(label = "@text/action-set-headlight-mode-label", description = "@text/action-set-headlight-mode-desc") + @RuleAction(label = "@text/action-set-work-area-label", description = "@text/action-set-work-area-desc") public void setWorkArea( @ActionInput(name = "workarea-id", label = "@text/action-input-workarea-id-label", description = "@text/action-input-workarea-id-desc") long workAreaId, @ActionInput(name = "enable", label = "@text/action-input-enable-label", description = "@text/action-input-enable-desc") boolean enable, diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 5fce8ea4a7196..adf6c18cd3aa2 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -548,7 +548,7 @@ public void sendAutomowerWorkAreaCuttingHeight(int index, byte cuttingHeight) { * Sends WorkArea Settings to the automower * * @param workAreaId Id of WorkArea - * @param enable CuttingHeight of the WorkArea + * @param enable Work area enable or disabled * @param cuttingHeight CuttingHeight of the WorkArea */ public void sendAutomowerWorkArea(long workAreaId, boolean enable, byte cuttingHeight) { diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties index 982b3cf5478cb..ba97b68183472 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties @@ -7,7 +7,6 @@ addon.automower.description = Binding to interact with Husquvarna Automower robo thing-type.automower.automower.label = Automower thing-type.automower.automower.description = An automatic lawn mower -thing-type.automower.automower.channel.last-position.description = Last Position thing-type.automower.bridge.label = Automower Connect Bridge thing-type.automower.bridge.description = The bridge to communicate with the Automower Connect API @@ -15,6 +14,8 @@ thing-type.automower.bridge.description = The bridge to communicate with the Aut thing-type.config.automower.automower.mowerId.label = Automower Id thing-type.config.automower.automower.mowerId.description = The Id of an automower as used by the Automower Connect Api to identify a mower +thing-type.config.automower.automower.mowerZoneId.label = TimeZone +thing-type.config.automower.automower.mowerZoneId.description = Time zone of the mower (e.g. Europe/Berlin) thing-type.config.automower.automower.pollingInterval.label = Polling Interval thing-type.config.automower.automower.pollingInterval.description = How often the current automower state should be polled in seconds thing-type.config.automower.bridge.appKey.label = Application Key @@ -29,7 +30,7 @@ thing-type.config.automower.bridge.pollingInterval.description = How often the a channel-type.automower.activityType.label = Activity channel-type.automower.activityType.description = The current activity channel-type.automower.activityType.state.option.UNKNOWN = Unknown -channel-type.automower.activityType.state.option.NOT_APPLICABLE = N/A +channel-type.automower.activityType.state.option.NOT_APPLICABLE = Not Applicable channel-type.automower.activityType.state.option.MOWING = Mowing channel-type.automower.activityType.state.option.GOING_HOME = Returning to charging station channel-type.automower.activityType.state.option.CHARGING = Charging @@ -38,12 +39,41 @@ channel-type.automower.activityType.state.option.PARKED_IN_CS = Parked in chargi channel-type.automower.activityType.state.option.STOPPED_IN_GARDEN = Stopped in garden channel-type.automower.batteryType.label = Battery channel-type.automower.batteryType.description = The battery level of the mower at the time of last update -channel-type.automower.calendarTasksType.label = Planner Info JSON -channel-type.automower.calendarTasksType.description = The channel providing a JSON with the information about the planner tasks. +channel-type.automower.calendarTasksDurationType.label = Duration +channel-type.automower.calendarTasksDurationType.description = Duration time +channel-type.automower.calendarTasksFridayType.label = Enabled on Fridays +channel-type.automower.calendarTasksFridayType.description = Enabled on Fridays +channel-type.automower.calendarTasksMondayType.label = Enabled on Mondays +channel-type.automower.calendarTasksMondayType.description = Enabled on Mondays +channel-type.automower.calendarTasksSaturdayType.label = Enabled on Saturdays +channel-type.automower.calendarTasksSaturdayType.description = Enabled on Saturdays +channel-type.automower.calendarTasksStartType.label = Start Time +channel-type.automower.calendarTasksStartType.description = Start time relative to midnight +channel-type.automower.calendarTasksSundayType.label = Enabled on Sundays +channel-type.automower.calendarTasksSundayType.description = Enabled on Sundays +channel-type.automower.calendarTasksThursdayType.label = Enabled on Thursdays +channel-type.automower.calendarTasksThursdayType.description = Enabled on Thursdays +channel-type.automower.calendarTasksTuesdayType.label = Enabled on Tuesdays +channel-type.automower.calendarTasksTuesdayType.description = Enabled on Tuesdays +channel-type.automower.calendarTasksWednesdayType.label = Enabled on Wednesdays +channel-type.automower.calendarTasksWednesdayType.description = Enabled on Wednesdays +channel-type.automower.calendarTasksWorkAreaIdType.label = Work Area Id of Calendar +channel-type.automower.calendarTasksWorkAreaIdType.description = Work Area Id mapped to this calendar +channel-type.automower.confirmError.label = Confirm Error +channel-type.automower.confirmError.description = Confirm current non fatal error channel-type.automower.errorCodeType.label = Error Code channel-type.automower.errorCodeType.description = The error code at the time of last update +channel-type.automower.errorConfirmableType.label = Error Confirmable +channel-type.automower.errorConfirmableType.description = If the mower has an Error Code this attribute states if the error is confirmable channel-type.automower.errorTimestampType.label = Error Time channel-type.automower.errorTimestampType.description = The time when the error occurred +channel-type.automower.inactiveReasonType.label = Inactive Reason +channel-type.automower.inactiveReasonType.description = The current reason for being inactive +channel-type.automower.inactiveReasonType.state.option.NONE = No inactive reason +channel-type.automower.inactiveReasonType.state.option.PLANNING = The mower is planning a path or a work area +channel-type.automower.inactiveReasonType.state.option.SEARCHING_FOR_SATELLITES = Waiting for fix when using EPOS +channel-type.automower.lastPositionType.label = Last GPS Position +channel-type.automower.lastPositionType.description = Last GPS position of the mower channel-type.automower.lastUpdateType.label = Last Update channel-type.automower.lastUpdateType.description = The time when the mower sent the last update channel-type.automower.modeType.label = Mode @@ -63,16 +93,53 @@ channel-type.automower.parkUntilNextSchedule.label = Park Until Next Schedule channel-type.automower.parkUntilNextSchedule.description = Park until next schedule channel-type.automower.pause.label = Pause channel-type.automower.pause.description = Pause the mower now until manual resume +channel-type.automower.plannerExternalReasonType.label = External Reason +channel-type.automower.plannerExternalReasonType.description = The channel providing an external reason that restrics current planner operation. channel-type.automower.plannerNextStartTimestampType.label = Next Auto Start channel-type.automower.plannerNextStartTimestampType.description = The channel providing the time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL. channel-type.automower.plannerOverrideActionType.label = Override Action channel-type.automower.plannerOverrideActionType.description = The channel providing an action that overrides current planner operation. +channel-type.automower.plannerRestrictedReasonType.label = Restricted Reason +channel-type.automower.plannerRestrictedReasonType.description = The channel providing a reason that restrics current planner operation. +channel-type.automower.plannerRestrictedReasonType.state.option.NONE = No restricted reason +channel-type.automower.plannerRestrictedReasonType.state.option.WEEK_SCHEDULE = Restricted by week schedule +channel-type.automower.plannerRestrictedReasonType.state.option.PARK_OVERRIDE = Forced to park +channel-type.automower.plannerRestrictedReasonType.state.option.SENSOR = Restricted by sensor +channel-type.automower.plannerRestrictedReasonType.state.option.DAILY_LIMIT = Restricted by daily limit +channel-type.automower.plannerRestrictedReasonType.state.option.FOTA = Restricted by FOTA transfer +channel-type.automower.plannerRestrictedReasonType.state.option.FROST = Restricted by frost sensor +channel-type.automower.plannerRestrictedReasonType.state.option.ALL_WORK_AREAS_COMPLETED = Restricted: All work areas completed +channel-type.automower.plannerRestrictedReasonType.state.option.EXTERNAL = Restricted by external reason channel-type.automower.positionType.label = GPS Position -channel-type.automower.positionType.description = The channel providing a waypoint of the mower's activity. +channel-type.automower.positionType.description = A waypoint of the mower's activity channel-type.automower.resumeSchedule.label = Resume Schedule channel-type.automower.resumeSchedule.description = Resume schedule +channel-type.automower.settingCuttingHeightType.label = Cutting Height +channel-type.automower.settingCuttingHeightType.description = Prescaled cutting height, Range: 1-9 +channel-type.automower.settingHeadlightModeType.label = Headlight Mode +channel-type.automower.settingHeadlightModeType.description = Information about headlights channel-type.automower.start.label = Start with Duration channel-type.automower.start.description = Start for a duration in minutes +channel-type.automower.statCuttingBladeUsageTimeType.label = Cutting Blade Usage Time +channel-type.automower.statCuttingBladeUsageTimeType.description = The time since the last reset of the cutting blade usage counter +channel-type.automower.statNumberOfChargingCyclesType.label = Number of Charging Cycles +channel-type.automower.statNumberOfChargingCyclesType.description = Number of charging cycles +channel-type.automower.statNumberOfCollisionsType.label = Number of Collisions +channel-type.automower.statNumberOfCollisionsType.description = The total number of collisions +channel-type.automower.statTotalChargingTimeType.label = Total Charging Time +channel-type.automower.statTotalChargingTimeType.description = Total charging time +channel-type.automower.statTotalCuttingPercentType.label = Relative Total Cutting Time +channel-type.automower.statTotalCuttingPercentType.description = Total cutting time in percent +channel-type.automower.statTotalCuttingTimeType.label = Total Cutting Time +channel-type.automower.statTotalCuttingTimeType.description = Total cutting time +channel-type.automower.statTotalDriveDistanceType.label = Total Drive Distance +channel-type.automower.statTotalDriveDistanceType.description = Total driven distance +channel-type.automower.statTotalRunningTimeType.label = Total Running Time +channel-type.automower.statTotalRunningTimeType.description = The total running time (the wheel motors have been running) +channel-type.automower.statTotalSearchingPercentType.label = Relative Total Searching Time +channel-type.automower.statTotalSearchingPercentType.description = The total searching time in percent +channel-type.automower.statTotalSearchingTimeType.label = Total Searching Time +channel-type.automower.statTotalSearchingTimeType.description = The total searching time channel-type.automower.stateType.label = State channel-type.automower.stateType.description = The current state channel-type.automower.stateType.state.option.UNKNOWN = Unknown @@ -81,20 +148,57 @@ channel-type.automower.stateType.state.option.PAUSED = Paused by user channel-type.automower.stateType.state.option.IN_OPERATION = Working channel-type.automower.stateType.state.option.WAIT_UPDATING = Downloading new firmware channel-type.automower.stateType.state.option.WAIT_POWER_UP = Booting mower -channel-type.automower.stateType.state.option.RESTRICTED_NONE = Waiting +channel-type.automower.stateType.state.option.RESTRICTED_NONE = No restricted reason channel-type.automower.stateType.state.option.RESTRICTED_WEEK_SCHEDULE = Restricted by week schedule channel-type.automower.stateType.state.option.RESTRICTED_PARK_OVERRIDE = Forced to park channel-type.automower.stateType.state.option.RESTRICTED_SENSOR = Restricted by sensor channel-type.automower.stateType.state.option.RESTRICTED_DAILY_LIMIT = Restricted by daily limit +channel-type.automower.stateType.state.option.RESTRICTED_FOTA = Restricted by FOTA transfer +channel-type.automower.stateType.state.option.RESTRICTED_FROST = Restricted by frost sensor +channel-type.automower.stateType.state.option.RESTRICTED_ALL_WORK_AREAS_COMPLETED = Restricted: All work areas completed +channel-type.automower.stateType.state.option.RESTRICTED_EXTERNAL = Restricted by external reason channel-type.automower.stateType.state.option.OFF = Off -channel-type.automower.stateType.state.option.STOPPED = Stopped- Manual intervention required +channel-type.automower.stateType.state.option.STOPPED = Stopped: Manual intervention required channel-type.automower.stateType.state.option.ERROR = Error channel-type.automower.stateType.state.option.FATAL_ERROR = Fatal error channel-type.automower.stateType.state.option.ERROR_AT_POWER_UP = Boot error +channel-type.automower.workAreaIdType.label = Work Area Id +channel-type.automower.workAreaIdType.description = Id of the active work area +channel-type.automower.workAreaType.label = Work Area Name +channel-type.automower.workAreaType.description = Name of the active work area +channel-type.automower.workareaCuttingHeightType.label = Cutting height in percent +channel-type.automower.workareaCuttingHeightType.description = Cutting height in percent. 0-100 +channel-type.automower.workareaEnabledType.label = Work Area Enabled +channel-type.automower.workareaEnabledType.description = If the work area is enabled or disabled +channel-type.automower.workareaIdType.label = Work Area Id +channel-type.automower.workareaIdType.description = Id of the Work Area +channel-type.automower.workareaLastTimeCompletedType.label = Last Time Completed +channel-type.automower.workareaLastTimeCompletedType.description = Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only. +channel-type.automower.workareaNameType.label = Work Area Name +channel-type.automower.workareaNameType.description = Name of the work area +channel-type.automower.workareaProgressType.label = Work Area Progress +channel-type.automower.workareaProgressType.description = The progress on a work area. EPOS mowers and systematic mowing work areas only. +channel-type.automower.zoneDirtyType.label = Stayout zones Dirty Flag +channel-type.automower.zoneDirtyType.description = If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone. +channel-type.automower.zoneEnabledType.label = Stayout Zone Enabled +channel-type.automower.zoneEnabledType.description = If the Stayout zone is enabled, the Automower® will not access the zone +channel-type.automower.zoneIdType.label = Stayout Zone Id +channel-type.automower.zoneIdType.description = Id of the Stayout zone +channel-type.automower.zoneNameType.label = Stayout Zone Name +channel-type.automower.zoneNameType.description = The name of the Stayout zone + +# thing types + +thing-type.automower.automower.channel.last-position.description = Last Position + +# channel types + +channel-type.automower.calendarTasksType.label = Planner Info JSON +channel-type.automower.calendarTasksType.description = The channel providing a JSON with the information about the planner tasks. # actions -action-input-duration-label = Duration +action-input-duration-label = duration action-input-duration-desc = The duration of the automower command in minutes action-park-label = park the automower action-park-desc = Parks the automower for a defined amount of time, overriding its schedule. @@ -108,6 +212,50 @@ action-resumeschedule-label = resume the schedule action-resumeschedule-desc = Resumes the schedule for the automower. action-start-label = start the automower action-start-desc = Starts the automower for a defined amount of time, overriding its schedule. +action-confirm-error-label = confirm error +action-confirm-error-desc = Confirm current non fatal error. +action-set-settings-label = update automower settings +action-set-settings-desc = Sends a set of Settings to the automower. +action-input-cutting-height-label = cutting height +action-input-cutting-height-desc = Prescaled cutting height, Range: 1-9. +action-input-headlight-mode-label = headlight mode +action-input-headlight-mode-desc = Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT). +action-set-work-area-label = update work area configuration +action-set-work-area-desc = Update the work area configuration of the specified area. +action-input-workarea-id-label = work area id +action-input-workarea-id-desc = Id of WorkArea to be updated. +action-input-enable-label = enable +action-input-enable-desc = Enable or disable WorkArea. +action-input-cutting-height-label = cutting height +action-input-cutting-height-desc = Cutting height of the WorkArea. +action-set-stayoutzone-label = update stayout zone configuration +action-set-stayoutzone-desc = Update the stayout zone configuration of the specified zone. +action-input-zone-id-label = zone id +action-input-zone-id-desc = Id of the stayout zone to be updated. +action-input-enable-label = enable +action-input-enable-desc = Enable or disable stayout zone. +action-set-calendartask-label = update calendar task configuration +action-set-calendartask-desc = Update the calendar task configuration of the mower or specified work area. +action-input-workarea-id-label = work area id +action-input-workarea-id-desc = Id of WorkArea where the calendar task configuration shall be updated (null if mower doesn't support work areas). +action-input-start-label = start +action-input-start-desc = Start time relative to midnight (minutes). +action-input-duration-label = duration +action-input-duration-desc = Duration time (minutes). +action-input-monday-label = monday[] +action-input-monday-desc = Enabled on Mondays (array with the number of calendar tasks). +action-input-tuesday-label = tuesday[] +action-input-tuesday-desc = Enabled on Tuesdays (array with the number of calendar tasks). +action-input-wednesday-label = wednesday[] +action-input-wednesday-desc = Enabled on Wednesdays (array with the number of calendar tasks). +action-input-thursday-label = thursday[] +action-input-thursday-desc = Enabled on Thursdays (array with the number of calendar tasks). +action-input-friday-label = friday[] +action-input-friday-desc = Enabled on Fridays (array with the number of calendar tasks). +action-input-saturday-label = saturday +action-input-saturday-desc = Enabled on Saturdays (array with the number of calendar tasks). +action-input-sunday-label = sunday +action-input-sunday-desc = Enabled on Sunday (array with the number of calendar tasks). # status messages From e20c4be37600cde7c5b8184284e8a99ff3839def Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Wed, 13 Nov 2024 21:46:40 +0100 Subject: [PATCH 31/35] further improvements after testing Signed-off-by: Michael Weger --- .../internal/things/AutomowerHandler.java | 105 ++++++++++-------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index adf6c18cd3aa2..07c12b8a9f3a7 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -95,7 +95,8 @@ public class AutomowerHandler extends BaseThingHandler { private long lastQueryTimeMs = 0L; private @Nullable ScheduledFuture automowerPollingJob; - private long maxQueryFrequencyNanos = TimeUnit.MINUTES.toNanos(1); + // Max 1 request per second and appKey. + private long maxQueryFrequencyNanos = TimeUnit.SECONDS.toNanos(1); private @Nullable Mower mowerState; @@ -365,7 +366,9 @@ public void sendAutomowerCalendarTask(Long workAreaId, short[] start, short[] du calendarTask.setFriday(friday[i]); calendarTask.setSaturday(saturday[i]); calendarTask.setSunday(sunday[i]); - + if (workAreaId != null) { + calendarTask.setWorkAreaId(workAreaId); + } calendarTaskArray.add(calendarTask); } @@ -401,58 +404,75 @@ public void sendAutomowerCalendarTask(Command command, String channelID) { if (isValidResult(mowerState)) { List calendarTasksFiltered; List calendarTasksAll = mowerState.getAttributes().getCalendar().getTasks(); + int indexFiltered = 0; if (mowerState.getAttributes().getCapabilities().hasWorkAreas()) { // only set the Tasks of the current WorkArea calendarTasksFiltered = new ArrayList<>(); + int i = 0; for (CalendarTask calendarTask : calendarTasksAll) { if (calendarTask.getWorkAreaId().equals(calendarTasksAll.get(index).getWorkAreaId())) { - calendarTasksFiltered.add(calendarTask); + if (index == i) { + // remember index and create deep copy + indexFiltered = calendarTasksFiltered.size(); + + CalendarTask calendarTask2 = new CalendarTask(); + calendarTask2.setStart(calendarTask.getStart()); + calendarTask2.setDuration(calendarTask.getDuration()); + calendarTask2.setMonday(calendarTask.getMonday()); + calendarTask2.setTuesday(calendarTask.getTuesday()); + calendarTask2.setWednesday(calendarTask.getWednesday()); + calendarTask2.setThursday(calendarTask.getThursday()); + calendarTask2.setFriday(calendarTask.getFriday()); + calendarTask2.setSaturday(calendarTask.getSaturday()); + calendarTask2.setSunday(calendarTask.getSunday()); + calendarTask2.setWorkAreaId(calendarTask.getWorkAreaId()); + calendarTasksFiltered.add(calendarTask2); + } else { + // no deep copy required for the lines that are not updated + calendarTasksFiltered.add(calendarTask); + } } + i++; } } else { + indexFiltered = index; calendarTasksFiltered = calendarTasksAll; } - if (calendarTasksFiltered.get(index) != null) { - CalendarTask calendarTask = calendarTasksAll.get(index); // possible race condition: cache is update in - // the background via cyclic tasks before the - // new request is sent out - deep clone of - // object required - // Different handling required - hasWorkAreas - // ON/OFF - if (command instanceof DecimalType cmd) { + CalendarTask calendarTask = calendarTasksFiltered.get(indexFiltered); + + if (command instanceof DecimalType cmd) { + if ("start".equals(param)) { + calendarTask.setStart(cmd.shortValue()); + } else if ("duration".equals(param)) { + calendarTask.setDuration(cmd.shortValue()); + } + } else if (command instanceof QuantityType cmd) { + cmd = cmd.toUnit("min"); + if (cmd != null) { if ("start".equals(param)) { calendarTask.setStart(cmd.shortValue()); } else if ("duration".equals(param)) { calendarTask.setDuration(cmd.shortValue()); } - } else if (command instanceof QuantityType cmd) { - cmd = cmd.toUnit("min"); - if (cmd != null) { - if ("start".equals(param)) { - calendarTask.setStart(cmd.shortValue()); - } else if ("duration".equals(param)) { - calendarTask.setDuration(cmd.shortValue()); - } - } - } else if (command instanceof OnOffType cmd) { - boolean day = ((cmd == OnOffType.ON) ? true : false); - - if ("monday".equals(param)) { - calendarTask.setMonday(day); - } else if ("tuesday".equals(param)) { - calendarTask.setTuesday(day); - } else if ("wednesday".equals(param)) { - calendarTask.setWednesday(day); - } else if ("thursday".equals(param)) { - calendarTask.setThursday(day); - } else if ("friday".equals(param)) { - calendarTask.setFriday(day); - } else if ("saturday".equals(param)) { - calendarTask.setSaturday(day); - } else if ("sunday".equals(param)) { - calendarTask.setSunday(day); - } + } + } else if (command instanceof OnOffType cmd) { + boolean day = ((cmd == OnOffType.ON) ? true : false); + + if ("monday".equals(param)) { + calendarTask.setMonday(day); + } else if ("tuesday".equals(param)) { + calendarTask.setTuesday(day); + } else if ("wednesday".equals(param)) { + calendarTask.setWednesday(day); + } else if ("thursday".equals(param)) { + calendarTask.setThursday(day); + } else if ("friday".equals(param)) { + calendarTask.setFriday(day); + } else if ("saturday".equals(param)) { + calendarTask.setSaturday(day); + } else if ("sunday".equals(param)) { + calendarTask.setSunday(day); } String id = automowerId.get(); @@ -554,11 +574,6 @@ public void sendAutomowerWorkAreaCuttingHeight(int index, byte cuttingHeight) { public void sendAutomowerWorkArea(long workAreaId, boolean enable, byte cuttingHeight) { logger.debug("Sending WorkArea: workAreaId {}, enable {}, cuttingHeight {}", workAreaId, enable, cuttingHeight); if (isValidResult(mowerState)) { - // update local cache ... - WorkArea workArea = getWorkAreaById(mowerState, workAreaId); - workArea.setEnabled(enable); - workArea.setCuttingHeight(cuttingHeight); - // ... as well as request MowerWorkAreaAttributes workAreaAttributes = new MowerWorkAreaAttributes(); workAreaAttributes.setEnable(enable); workAreaAttributes.setCuttingHeight(cuttingHeight); @@ -623,10 +638,6 @@ public void sendAutomowerSettings(byte cuttingHeight, HeadlightMode headlightMod headlight.setHeadlightMode(headlightMode); settings.setHeadlight(headlight); - // update local cache ... - mowerState.getAttributes().getSettings().setCuttingHeight(cuttingHeight); - mowerState.getAttributes().getSettings().getHeadlight().setHeadlightMode(headlightMode); - String id = automowerId.get(); try { AutomowerBridge automowerBridge = getAutomowerBridge(); From 9889affe98713d2913a49ffbfa4834b28501592b Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 18 Nov 2024 21:26:52 +0100 Subject: [PATCH 32/35] API release 2024-10-14 (https://developer.husqvarnagroup.cloud/apis/automower-connect-api?tab=releases) Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 56 ++++++++++--------- .../internal/AutomowerBindingConstants.java | 3 +- .../internal/actions/AutomowerActions.java | 14 +++++ .../internal/bridge/AutomowerBridge.java | 10 ++++ .../automowerconnect/AutomowerConnectApi.java | 22 ++++++++ .../api/automowerconnect/dto/Statistics.java | 18 ++++++ .../internal/things/AutomowerHandler.java | 36 ++++++++++-- .../OH-INF/i18n/automower.properties | 14 ++++- .../resources/OH-INF/thing/thing-types.xml | 30 ++++++---- 9 files changed, 157 insertions(+), 46 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index b2785445f07a6..4d63d42cf1cec 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -24,7 +24,7 @@ Once the bridge is created and configured, openHAB will automatically discover a - pollingInterval (optional): How often the bridge state should be queried in seconds. Default is 1h (3600s) Keep in mind that the status of the bridge should not be queried too often. -According to the Husqvarna documentation not more than 10000 requests per month and application key are allowed. +According to the Husqvarna documentation not more than 10000 requests per month / 1 request per second and application key are allowed. With the default value of 1h this would mean ~720 requests per month for the bridge state `automower:` @@ -33,7 +33,7 @@ With the default value of 1h this would mean ~720 requests per month for the bri - pollingInterval (optional): How often the current Automower® state should be polled in seconds. Default is 10min (600s) Keep in mind that the status of the Automower® should not be queried too often. -According to the Husqvarna documentation, no more than 10000 requests per month and application key are allowed. +According to the Husqvarna documentation not more than 10000 requests per month / 1 request per second and application key are allowed. With the default value of 10min this would mean ~4300 requests per month per single Automower® ## Channels @@ -51,16 +51,17 @@ With the default value of 10min this would mean ~4300 requests per month per sin | work-area | String | R | Name of the active work area | | last-update | DateTime | R | The time when the Automower® updated its states | | battery | Number:Dimensionless | R | The battery state of charge in percent | -| error-code | Number | R | The current error code | +| error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | | error-timestamp | DateTime | R | The timestamp when the current error occurred | -| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | +| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | | planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | | planner-override-action | String | R | The action that overrides current planner operation | | planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | | planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | | setting-cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | | setting-headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | -| stat-cutting-blade-usage-time | Number:Time | R | The time since the last reset of the cutting blade usage counter | +| stat-cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | +| stat-down-time | Number:Time | R | The time the mower has been disconnected from the cloud | | stat-number-of-charging-cycles | Number | R | Number of charging cycles | | stat-number-of-collisions | Number | R | The total number of collisions | | stat-total-charging-time | Number:Time | R | Total charging time | @@ -70,6 +71,7 @@ With the default value of 10min this would mean ~4300 requests per month per sin | stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | | stat-total-searching-time | Number:Length | R | The total searching time | | stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | +| stat-up-time | Number:Time | R | The time the mower has been connected to the cloud | ### Calendar Tasks Channels @@ -134,33 +136,33 @@ These channels hold the different Work Area configurations. Right now a maximum ### Command Channels -| channel | type | access mode | description | -|-----------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| start | Number | W | Starts the Automower® for a duration | -| resume_schedule | Switch | W | Resumes the Automower® schedule | -| pause | Switch | W | Pause the Automower® | -| park | Number | W | Park the Automower® for a duration | -| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | -| park_until_further_notice | Switch | W | Park the Automower® until further notice | -| confirm_error | Switch | W | Confirm current non fatal error | +| channel | type | access mode | description | +|---------------------------|----------|-------------|------------------------------------------------| +| start | Number | W | Starts the Automower® for a duration | +| resume_schedule | Switch | W | Resumes the Automower® schedule | +| pause | Switch | W | Pause the Automower® | +| park | Number | W | Park the Automower® for a duration | +| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | +| park_until_further_notice | Switch | W | Park the Automower® until further notice | ## Actions The following actions are available for `automower` things: -| action name | arguments | description | -|------------------------|------------------|-------------------------------------------------------------------------------------------------| -| start | `duration (int)` | Starts the Automower® for the given duration (minutes), overriding the schedule | -| pause | - | Pauses the Automower® wherever it is currently located | -| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | -| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | -| park | `duration (int)` | Parks the Automower® for the given duration (minutes), overriding the schedule | -| resumeSchedule | - | Resumes the schedule for the Automower® | -| confirmError | - | Confirm current non fatal error | -| setSettings | `byte cuttingHeight`
`String headlightMode` | Update Automower® settings | -| setWorkArea | `long workAreaId`
`boolean enable`
`byte cuttingHeight` | Update work area settings | -| setStayOutZone | `String zoneId`
`boolean enable` | Enable or disable stay-out zone | -| setCalendarTask | `Long workAreaId` (optional, set to `null` if the mower doesn't support work areas)
`short[] start`
`short[] duration`
`boolean[] monday`
`boolean[] tuesday`
`boolean[] wednesday`
`boolean[] thursday`
`boolean[] friday`
`boolean[] saturday`
`boolean[] sunday` | Update calendar task settings | +| action name | arguments | description | +|----------------------------|------------------|-------------------------------------------------------------------------------------------------| +| start | `duration (int)` | Starts the Automower® for the given duration (minutes), overriding the schedule | +| pause | - | Pauses the Automower® wherever it is currently located | +| parkUntilNextSchedule | - | Parks the Automower®, fully charges it and starts afterwards according to the schedule | +| parkUntilFurtherNotice | - | Parks the Automower® until it is started again by the start action or the schedule gets resumed | +| park | `duration (int)` | Parks the Automower® for the given duration (minutes), overriding the schedule | +| resumeSchedule | - | Resumes the schedule for the Automower® | +| confirmError | - | Confirm current non fatal error | +| resetCuttingBladeUsageTime | - | Reset the cutting blade usage time | +| setSettings | `byte cuttingHeight`
`String headlightMode` | Update Automower® settings | +| setWorkArea | `long workAreaId`
`boolean enable`
`byte cuttingHeight` | Update work area settings | +| setStayOutZone | `String zoneId`
`boolean enable` | Enable or disable stay-out zone | +| setCalendarTask | `Long workAreaId` (optional, set to `null` if the mower doesn't support work areas)
`short[] start`
`short[] duration`
`boolean[] monday`
`boolean[] tuesday`
`boolean[] wednesday`
`boolean[] thursday`
`boolean[] friday`
`boolean[] saturday`
`boolean[] sunday` | Update calendar task settings | ## Full Example diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 185675951391d..9d32aa9d9d3a5 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -69,6 +69,7 @@ public class AutomowerBindingConstants { // this in a future release public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATISTIC + "stat-cutting-blade-usage-time"; + public static final String CHANNEL_STATISTIC_DOWN_TIME = GROUP_STATISTIC + "stat-down-time"; public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATISTIC + "stat-number-of-charging-cycles"; public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATISTIC + "stat-number-of-collisions"; @@ -80,6 +81,7 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATISTIC + "stat-total-searching-time"; public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATISTIC + "stat-total-searching-percent"; + public static final String CHANNEL_STATISTIC_UP_TIME = GROUP_STATISTIC + "stat-up-time"; // Calendar Task Channels ids public static final String GROUP_CALENDARTASKS = ""; // no channel group in use at the moment, we'll possibly @@ -234,7 +236,6 @@ public class AutomowerBindingConstants { public static final String CHANNEL_COMMAND_PARK = GROUP_COMMANDS + "park"; public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMANDS + "park_until_next_schedule"; public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMANDS + "park_until_further_notice"; - public static final String CHANNEL_COMMAND_CONFIRM_ERROR = GROUP_COMMANDS + "confirm_error"; // Automower properties public static final String AUTOMOWER_ID = "mowerId"; diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java index 70497a379562e..116bc2a111cee 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/actions/AutomowerActions.java @@ -147,6 +147,20 @@ public static void confirmError(ThingActions actions) { ((AutomowerActions) actions).confirmError(); } + @RuleAction(label = "@text/action-reset-cutting-blade-usage-time-label", description = "@text/action-reset-cutting-blade-usage-time-desc") + public void resetCuttingBladeUsageTime() { + AutomowerHandler automowerHandler = handler; + if (automowerHandler == null) { + logger.warn("Automower Action service ThingHandler is null!"); + } else { + automowerHandler.sendAutomowerResetCuttingBladeUsageTime(); + } + } + + public static void resetCuttingBladeUsageTime(ThingActions actions) { + ((AutomowerActions) actions).resetCuttingBladeUsageTime(); + } + @RuleAction(label = "@text/action-set-settings-label", description = "@text/action-set-settings-desc") public void setSettings( @ActionInput(name = "cutting-height", label = "@text/action-input-cutting-height-label", description = "@text/action-input-cutting-height-desc") byte cuttingHeight, diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java index 311d69e153cd9..bce4c21b81cda 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/bridge/AutomowerBridge.java @@ -173,6 +173,16 @@ public void sendAutomowerConfirmError(String id) throws AutomowerCommunicationEx automowerApi.sendConfirmError(appKey, authenticate().getAccessToken(), id); } + /** + * Reset the cutting blade usage time + * + * @param id The id of the mower + * @throws AutomowerCommunicationException In case the query cannot be executed successfully + */ + public void sendAutomowerResetCuttingBladeUsageTime(String id) throws AutomowerCommunicationException { + automowerApi.sendResetCuttingBladeUsageTime(appKey, authenticate().getAccessToken(), id); + } + /** * Enable or disable stay out zone * diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java index 3d837c45e26ba..71809f3d3780a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/AutomowerConnectApi.java @@ -33,6 +33,8 @@ import org.openhab.binding.automower.internal.rest.api.automowerconnect.dto.MowerWorkAreaRequest; import org.openhab.binding.automower.internal.rest.exceptions.AutomowerCommunicationException; import org.openhab.binding.automower.internal.rest.exceptions.UnauthorizedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.JsonSyntaxException; @@ -43,6 +45,9 @@ */ @NonNullByDefault public class AutomowerConnectApi extends HusqvarnaApi { + + private final Logger logger = LoggerFactory.getLogger(AutomowerConnectApi.class); + public AutomowerConnectApi(HttpClient httpClient) { super(httpClient); } @@ -74,6 +79,7 @@ public void sendCommand(String appKey, String token, String id, MowerCommandRequ final Request request = getHttpClient().newRequest(getBaseUrl() + "/mowers/" + id + "/actions"); request.method(HttpMethod.POST); + logger.trace("sendCommand: {}", gson.toJson(command)); request.content(new StringContentProvider(gson.toJson(command))); ContentResponse response = executeRequest(appKey, token, request); @@ -92,6 +98,7 @@ public void sendCalendar(String appKey, String token, String id, boolean hasWork final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.POST); + logger.trace("sendCalendar: {}", gson.toJson(calendar)); request.content(new StringContentProvider(gson.toJson(calendar))); ContentResponse response = executeRequest(appKey, token, request); @@ -106,6 +113,7 @@ public void sendSettings(String appKey, String token, String id, MowerSettingsRe final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.POST); + logger.trace("sendSettings: {}", gson.toJson(settings)); request.content(new StringContentProvider(gson.toJson(settings))); ContentResponse response = executeRequest(appKey, token, request); @@ -124,6 +132,18 @@ public void sendConfirmError(String appKey, String token, String id) throws Auto checkForError(response, response.getStatus()); } + public void sendResetCuttingBladeUsageTime(String appKey, String token, String id) + throws AutomowerCommunicationException { + String url; + url = getBaseUrl() + "/mowers/" + id + "/statistics/resetCuttingBladeUsageTime"; + final Request request = getHttpClient().newRequest(url); + request.method(HttpMethod.POST); + + ContentResponse response = executeRequest(appKey, token, request); + + checkForError(response, response.getStatus()); + } + public void sendStayOutZone(String appKey, String token, String id, String zoneId, MowerStayOutZoneRequest zoneRequest) throws AutomowerCommunicationException { String url; @@ -131,6 +151,7 @@ public void sendStayOutZone(String appKey, String token, String id, String zoneI final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.PATCH); + logger.trace("sendStayOutZone: {}", gson.toJson(zoneRequest)); request.content(new StringContentProvider(gson.toJson(zoneRequest))); ContentResponse response = executeRequest(appKey, token, request); @@ -145,6 +166,7 @@ public void sendWorkArea(String appKey, String token, String id, long workAreaId final Request request = getHttpClient().newRequest(url); request.method(HttpMethod.PATCH); + logger.trace("sendWorkArea: {}", gson.toJson(workAreaRequest)); request.content(new StringContentProvider(gson.toJson(workAreaRequest))); ContentResponse response = executeRequest(appKey, token, request); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java index 63ca68fcd38ac..dff8ebc1b29b1 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/rest/api/automowerconnect/dto/Statistics.java @@ -17,6 +17,7 @@ */ public class Statistics { private long cuttingBladeUsageTime; + private long downTime; private int numberOfChargingCycles; private int numberOfCollisions; private long totalChargingTime; @@ -24,6 +25,7 @@ public class Statistics { private long totalDriveDistance; // docu states totalDrivenDistance which does not work private long totalRunningTime; private long totalSearchingTime; + private long upTime; public long getCuttingBladeUsageTime() { return cuttingBladeUsageTime; @@ -33,6 +35,14 @@ public void setCuttingBladeUsageTime(long cuttingBladeUsageTime) { this.cuttingBladeUsageTime = cuttingBladeUsageTime; } + public long getDownTime() { + return downTime; + } + + public void setDownTime(long downTime) { + this.downTime = downTime; + } + public int getNumberOfChargingCycles() { return numberOfChargingCycles; } @@ -88,4 +98,12 @@ public long getTotalSearchingTime() { public void setTotalSearchingTime(long totalSearchingTime) { this.totalSearchingTime = totalSearchingTime; } + + public long getUpTime() { + return upTime; + } + + public void setUpTime(long upTime) { + this.upTime = upTime; + } } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index 07c12b8a9f3a7..d37d14dd44d88 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -155,11 +155,16 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (command instanceof StringType cmd) { sendAutomowerSettingsHeadlightMode(cmd.toString()); } - } else if (channelUID.getId().equals(CHANNEL_COMMAND_CONFIRM_ERROR)) { - if (command instanceof OnOffType cmd) { - if (cmd == OnOffType.ON) { + } else if (channelUID.getId().equals(CHANNEL_STATUS_ERROR_CODE)) { + if (command instanceof DecimalType cmd) { + if (cmd.equals(new DecimalType(0))) { sendAutomowerConfirmError(); - updateState(CHANNEL_COMMAND_CONFIRM_ERROR, OnOffType.OFF); + } + } + } else if (channelUID.getId().equals(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME)) { + if (command instanceof DecimalType cmd) { + if (cmd.equals(new DecimalType(0))) { + sendAutomowerResetCuttingBladeUsageTime(); } } } else { @@ -678,6 +683,25 @@ public void sendAutomowerConfirmError() { } } + /** + * Reset the cutting blade usage time + */ + public void sendAutomowerResetCuttingBladeUsageTime() { + logger.debug("Sending ResetCuttingBladeUsageTime"); + String id = automowerId.get(); + try { + AutomowerBridge automowerBridge = getAutomowerBridge(); + if (automowerBridge != null) { + automowerBridge.sendAutomowerResetCuttingBladeUsageTime(id); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/conf-error-no-bridge"); + } + } catch (AutomowerCommunicationException e) { + logger.warn("Unable to send ResetCuttingBladeUsageTime to automower: {}, Error: {}", id, e.getMessage()); + } + updateAutomowerState(); + } + private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } @@ -777,6 +801,8 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME, new QuantityType<>(mower.getAttributes().getStatistics().getCuttingBladeUsageTime(), Units.SECOND)); + updateState(CHANNEL_STATISTIC_DOWN_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getDownTime(), Units.SECOND)); updateState(CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES, new DecimalType(mower.getAttributes().getStatistics().getNumberOfChargingCycles())); updateState(CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS, @@ -807,6 +833,8 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT, new QuantityType<>(0, Units.PERCENT)); updateState(CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT, new QuantityType<>(0, Units.PERCENT)); } + updateState(CHANNEL_STATISTIC_UP_TIME, + new QuantityType<>(mower.getAttributes().getStatistics().getUpTime(), Units.SECOND)); if (mower.getAttributes().getLastPosition() != null) { updateState(LAST_POSITION, diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties index ba97b68183472..c34042d02d247 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties @@ -41,6 +41,7 @@ channel-type.automower.batteryType.label = Battery channel-type.automower.batteryType.description = The battery level of the mower at the time of last update channel-type.automower.calendarTasksDurationType.label = Duration channel-type.automower.calendarTasksDurationType.description = Duration time +channel-type.automower.calendarTasksDurationType.state.pattern = %1$tHh:%1$tMm channel-type.automower.calendarTasksFridayType.label = Enabled on Fridays channel-type.automower.calendarTasksFridayType.description = Enabled on Fridays channel-type.automower.calendarTasksMondayType.label = Enabled on Mondays @@ -49,6 +50,7 @@ channel-type.automower.calendarTasksSaturdayType.label = Enabled on Saturdays channel-type.automower.calendarTasksSaturdayType.description = Enabled on Saturdays channel-type.automower.calendarTasksStartType.label = Start Time channel-type.automower.calendarTasksStartType.description = Start time relative to midnight +channel-type.automower.calendarTasksStartType.state.pattern = %1$tHh:%1$tMm channel-type.automower.calendarTasksSundayType.label = Enabled on Sundays channel-type.automower.calendarTasksSundayType.description = Enabled on Sundays channel-type.automower.calendarTasksThursdayType.label = Enabled on Thursdays @@ -59,8 +61,6 @@ channel-type.automower.calendarTasksWednesdayType.label = Enabled on Wednesdays channel-type.automower.calendarTasksWednesdayType.description = Enabled on Wednesdays channel-type.automower.calendarTasksWorkAreaIdType.label = Work Area Id of Calendar channel-type.automower.calendarTasksWorkAreaIdType.description = Work Area Id mapped to this calendar -channel-type.automower.confirmError.label = Confirm Error -channel-type.automower.confirmError.description = Confirm current non fatal error channel-type.automower.errorCodeType.label = Error Code channel-type.automower.errorCodeType.description = The error code at the time of last update channel-type.automower.errorConfirmableType.label = Error Confirmable @@ -122,6 +122,9 @@ channel-type.automower.start.label = Start with Duration channel-type.automower.start.description = Start for a duration in minutes channel-type.automower.statCuttingBladeUsageTimeType.label = Cutting Blade Usage Time channel-type.automower.statCuttingBladeUsageTimeType.description = The time since the last reset of the cutting blade usage counter +channel-type.automower.statDownTimeType.label = Down Time +channel-type.automower.statDownTimeType.description = The time the mower has been disconnected from the cloud +channel-type.automower.statDownTimeType.state.pattern = %1$tHh:%1$tMm:%1$tSs channel-type.automower.statNumberOfChargingCyclesType.label = Number of Charging Cycles channel-type.automower.statNumberOfChargingCyclesType.description = Number of charging cycles channel-type.automower.statNumberOfCollisionsType.label = Number of Collisions @@ -140,6 +143,9 @@ channel-type.automower.statTotalSearchingPercentType.label = Relative Total Sear channel-type.automower.statTotalSearchingPercentType.description = The total searching time in percent channel-type.automower.statTotalSearchingTimeType.label = Total Searching Time channel-type.automower.statTotalSearchingTimeType.description = The total searching time +channel-type.automower.statUpTimeType.label = Up Time +channel-type.automower.statUpTimeType.description = The time the mower has been connected to the cloud +channel-type.automower.statUpTimeType.state.pattern = %1$tHh:%1$tMm:%1$tSs channel-type.automower.stateType.label = State channel-type.automower.stateType.description = The current state channel-type.automower.stateType.state.option.UNKNOWN = Unknown @@ -214,10 +220,12 @@ action-start-label = start the automower action-start-desc = Starts the automower for a defined amount of time, overriding its schedule. action-confirm-error-label = confirm error action-confirm-error-desc = Confirm current non fatal error. +action-reset-cutting-blade-usage-time-label = reset cutting blade usage time +action-reset-cutting-blade-usage-time-desc = Reset the cutting blade usage time. action-set-settings-label = update automower settings action-set-settings-desc = Sends a set of Settings to the automower. action-input-cutting-height-label = cutting height -action-input-cutting-height-desc = Prescaled cutting height, Range: 1-9. +action-input-cutting-height-desc = Prescaled cutting height, Range: 1-9. action-input-headlight-mode-label = headlight mode action-input-headlight-mode-desc = Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT). action-set-work-area-label = update work area configuration diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 09fc1544c7dc8..5504b7f40da8c 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -63,6 +63,7 @@ + @@ -72,6 +73,7 @@ + @@ -201,7 +203,6 @@ - @@ -497,7 +498,6 @@ Number The error code at the time of last update -
@@ -571,7 +571,14 @@ Number:Time The time since the last reset of the cutting blade usage counter - + + + + + Number:Time + + The time the mower has been disconnected from the cloud + @@ -637,6 +644,13 @@ + + Number:Time + + The time the mower has been connected to the cloud + + + Location @@ -656,13 +670,13 @@ Number:Time Start time relative to midnight - + Number:Time Duration time - + Switch @@ -814,10 +828,4 @@ Park and pause the mower schedule until manual resume - - - Switch - - Confirm current non fatal error - From 9f84dafe3cf368038740dada4e3ca21a011f5849 Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Mon, 18 Nov 2024 21:55:28 +0100 Subject: [PATCH 33/35] ResetCuttingBladeUsageTime with QuantityType Signed-off-by: Michael Weger --- .../binding/automower/internal/things/AutomowerHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index d37d14dd44d88..fd647cc08e07e 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -166,6 +166,10 @@ public void handleCommand(ChannelUID channelUID, Command command) { if (cmd.equals(new DecimalType(0))) { sendAutomowerResetCuttingBladeUsageTime(); } + } else if (command instanceof QuantityType cmd) { + if (cmd.toBigDecimal().intValue() == 0) { + sendAutomowerResetCuttingBladeUsageTime(); + } } } else { AutomowerCommand.fromChannelUID(channelUID).ifPresent(commandName -> { From 33280d8d46b31e783a201670b0a20fe9bc48943b Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Thu, 21 Nov 2024 19:04:51 +0100 Subject: [PATCH 34/35] added error message set several channels to advance added update.xml Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 113 ++++++------ .../internal/AutomowerBindingConstants.java | 169 ++++++++++++++++++ .../internal/things/AutomowerHandler.java | 15 +- .../OH-INF/i18n/automower.properties | 2 + .../resources/OH-INF/thing/thing-types.xml | 56 +++--- 5 files changed, 273 insertions(+), 82 deletions(-) diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 4d63d42cf1cec..3ce58fae16285 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -40,56 +40,57 @@ With the default value of 10min this would mean ~4300 requests per month per sin ### Status Channels -| channel | type | access mode | description | -|------------|----------|-------------|--------------| -| name | String | R | The name of the Automower® | -| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | -| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | -| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | -| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | -| work-area-id | Number | R | Id of the active work area | -| work-area | String | R | Name of the active work area | -| last-update | DateTime | R | The time when the Automower® updated its states | -| battery | Number:Dimensionless | R | The battery state of charge in percent | -| error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | -| error-timestamp | DateTime | R | The timestamp when the current error occurred | -| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | -| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | -| planner-override-action | String | R | The action that overrides current planner operation | -| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | -| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | -| setting-cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | -| setting-headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | -| stat-cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | -| stat-down-time | Number:Time | R | The time the mower has been disconnected from the cloud | -| stat-number-of-charging-cycles | Number | R | Number of charging cycles | -| stat-number-of-collisions | Number | R | The total number of collisions | -| stat-total-charging-time | Number:Time | R | Total charging time | -| stat-total-cutting-time | Number:Time | R | Total Cutting Time | -| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | -| stat-total-drive-distance | Number:Length | R | Total driven distance | -| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | -| stat-total-searching-time | Number:Length | R | The total searching time | -| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | -| stat-up-time | Number:Time | R | The time the mower has been connected to the cloud | +| channel | type | access mode | description | advanced | +|------------|----------|-------------|--------------|----------| +| name | String | R | The name of the Automower® | false | +| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | false | +| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | false | +| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | false | +| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | false | +| work-area-id | Number | R | Id of the active work area | true | +| work-area | String | R | Name of the active work area | false | +| last-update | DateTime | R | The time when the Automower® updated its states | false | +| battery | Number:Dimensionless | R | The battery state of charge in percent | false | +| error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | true | +| error-message | String | R | The current error message | false | +| error-timestamp | DateTime | R | The timestamp when the current error occurred | false | +| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | true | +| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | false | +| planner-override-action | String | R | The action that overrides current planner operation | true | +| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | false | +| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | true | +| setting-cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | false | +| setting-headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | false | +| stat-cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | false | +| stat-down-time | Number:Time | R | The time the mower has been disconnected from the cloud | true | +| stat-number-of-charging-cycles | Number | R | Number of charging cycles | false | +| stat-number-of-collisions | Number | R | The total number of collisions | false | +| stat-total-charging-time | Number:Time | R | Total charging time | false | +| stat-total-cutting-time | Number:Time | R | Total Cutting Time | false | +| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | false | +| stat-total-drive-distance | Number:Length | R | Total driven distance | false | +| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | false | +| stat-total-searching-time | Number:Length | R | The total searching time | false | +| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | false | +| stat-up-time | Number:Time | R | The time the mower has been connected to the cloud | true | ### Calendar Tasks Channels These channels hold the different Calendar Task configurations. Right now a maximum of 10 Calendar Tasks are supported by the binding. -| channel | type | access mode | description | -|------------|----------|-------------|--------------| -| calendartasks\-start | Number:Time | R/W | Start time relative to midnight | -| calendartasks\-duration | Number:Time | R/W | Duration time | -| calendartasks\-monday | Switch | R/W | Enabled on Mondays | -| calendartasks\-tuesday | Switch | R/W | Enabled on Tuesdays | -| calendartasks\-wednesday | Switch | R/W | Enabled on Wednesdays | -| calendartasks\-thursday | Switch | R/W | Enabled on Thursdays | -| calendartasks\-friday | Switch | R/W | Enabled on Fridays | -| calendartasks\-saturday | Switch | R/W | Enabled on Saturdays | -| calendartasks\-sunday | Switch | R/W | Enabled on Sundays | -| calendartasks\-workAreaId | Number | R | Work Area Id mapped to this calendar | -| calendartasks\-workArea | String | R | Name of the Work Area mapped to this calendar | +| channel | type | access mode | description | advanced | +|------------|----------|-------------|--------------|----------| +| calendartasks\-start | Number:Time | R/W | Start time relative to midnight | true | +| calendartasks\-duration | Number:Time | R/W | Duration time | true | +| calendartasks\-monday | Switch | R/W | Enabled on Mondays | true | +| calendartasks\-tuesday | Switch | R/W | Enabled on Tuesdays | true | +| calendartasks\-wednesday | Switch | R/W | Enabled on Wednesdays | true | +| calendartasks\-thursday | Switch | R/W | Enabled on Thursdays | true | +| calendartasks\-friday | Switch | R/W | Enabled on Fridays | true | +| calendartasks\-saturday | Switch | R/W | Enabled on Saturdays | true | +| calendartasks\-sunday | Switch | R/W | Enabled on Sundays | true | +| calendartasks\-workAreaId | Number | R | Work Area Id mapped to this calendar | true | +| calendartasks\-workArea | String | R | Name of the Work Area mapped to this calendar | true | \ ... 01-10 @@ -113,9 +114,9 @@ These channels hold the different Stayout Zone configurations. Right now a maxim | channel | type | access mode | description | advanced | |------------|----------|-------------|--------------|----------| | dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | -| zone\-id | String | R | Id of the Stayout zone | false | -| zone\-name | String | R | The name of the Stayout zone | false | -| zone\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | false | +| zone\-id | String | R | Id of the Stayout zone | true | +| zone\-name | String | R | The name of the Stayout zone | true | +| zone\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | true | \ ... 01-10 @@ -136,14 +137,14 @@ These channels hold the different Work Area configurations. Right now a maximum ### Command Channels -| channel | type | access mode | description | -|---------------------------|----------|-------------|------------------------------------------------| -| start | Number | W | Starts the Automower® for a duration | -| resume_schedule | Switch | W | Resumes the Automower® schedule | -| pause | Switch | W | Pause the Automower® | -| park | Number | W | Park the Automower® for a duration | -| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | -| park_until_further_notice | Switch | W | Park the Automower® until further notice | +| channel | type | access mode | description | advanced | +|---------------------------|----------|-------------|------------------------------------------------|----------| +| start | Number | W | Starts the Automower® for a duration | false | +| resume_schedule | Switch | W | Resumes the Automower® schedule | false | +| pause | Switch | W | Pause the Automower® | false | +| park | Number | W | Park the Automower® for a duration | false | +| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | false | +| park_until_further_notice | Switch | W | Park the Automower® until further notice | false | ## Actions diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index 9d32aa9d9d3a5..cdd354a8c11cd 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -13,7 +13,9 @@ package org.openhab.binding.automower.internal; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.core.thing.ThingTypeUID; @@ -47,6 +49,7 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATUS_WORK_AREA = GROUP_STATUS + "work-area"; public static final String CHANNEL_STATUS_BATTERY = GROUP_STATUS + "battery"; public static final String CHANNEL_STATUS_ERROR_CODE = GROUP_STATUS + "error-code"; + public static final String CHANNEL_STATUS_ERROR_MESSAGE = GROUP_STATUS + "error-message"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; public static final String CHANNEL_STATUS_ERROR_CONFIRMABLE = GROUP_STATUS + "error-confirmable"; @@ -247,4 +250,170 @@ public class AutomowerBindingConstants { public static final String AUTOMOWER_HAS_POSITION = "mowerHasPosition"; public static final String AUTOMOWER_HAS_STAY_OUT_ZONES = "mowerHasStayOutZones"; public static final String AUTOMOWER_HAS_WORK_AREAS = "mowerHasWorkAreas"; + + public static final Map ERROR = new HashMap<>() { + { + put(0, "No message"); + put(1, "Outside working area"); + put(2, "No loop signal"); + put(3, "Wrong loop signal"); + put(4, "Loop sensor problem, front"); + put(5, "Loop sensor problem, rear"); + put(6, "Loop sensor problem, left"); + put(7, "Loop sensor problem, right"); + put(8, "Wrong PIN code"); + put(9, "Trapped"); + put(10, "Upside down"); + put(11, "Low battery"); + put(12, "Empty battery"); + put(13, "No drive"); + put(14, "Mower lifted"); + put(15, "Lifted"); + put(16, "Stuck in charging station"); + put(17, "Charging station blocked"); + put(18, "Collision sensor problem, rear"); + put(19, "Collision sensor problem, front"); + put(20, "Wheel motor blocked, right"); + put(21, "Wheel motor blocked, left"); + put(22, "Wheel drive problem, right"); + put(23, "Wheel drive problem, left"); + put(24, "Cutting system blocked"); + put(25, "Cutting system blocked"); + put(26, "Invalid sub-device combination"); + put(27, "Settings restored"); + put(28, "Memory circuit problem"); + put(29, "Slope too steep"); + put(30, "Charging system problem"); + put(31, "STOP button problem"); + put(32, "Tilt sensor problem"); + put(33, "Mower tilted"); + put(34, "Cutting stopped - slope too steep"); + put(35, "Wheel motor overloaded, right"); + put(36, "Wheel motor overloaded, left"); + put(37, "Charging current too high"); + put(38, "Electronic problem"); + put(39, "Cutting motor problem"); + put(40, "Limited cutting height range"); + put(41, "Unexpected cutting height adj"); + put(42, "Limited cutting height range"); + put(43, "Cutting height problem, drive"); + put(44, "Cutting height problem, curr"); + put(45, "Cutting height problem, dir"); + put(46, "Cutting height blocked"); + put(47, "Cutting height problem"); + put(48, "No response from charger"); + put(49, "Ultrasonic problem"); + put(50, "Guide 1 not found"); + put(51, "Guide 2 not found"); + put(52, "Guide 3 not found"); + put(53, "GPS navigation problem"); + put(54, "Weak GPS signal"); + put(55, "Difficult finding home"); + put(56, "Guide calibration accomplished"); + put(57, "Guide calibration failed"); + put(58, "Temporary battery problem"); + put(59, "Temporary battery problem"); + put(60, "Temporary battery problem"); + put(61, "Temporary battery problem"); + put(62, "Temporary battery problem"); + put(63, "Temporary battery problem"); + put(64, "Temporary battery problem"); + put(65, "Temporary battery problem"); + put(66, "Battery problem"); + put(67, "Battery problem"); + put(68, "Temporary battery problem"); + put(69, "Alarm! Mower switched off"); + put(70, "Alarm! Mower stopped"); + put(71, "Alarm! Mower lifted"); + put(72, "Alarm! Mower tilted"); + put(73, "Alarm! Mower in motion"); + put(74, "Alarm! Outside geofence"); + put(75, "Connection changed"); + put(76, "Connection NOT changed"); + put(77, "Com board not available"); + put(78, "Slipped - Mower has Slipped.Situation not solved with moving pattern"); + put(79, "Invalid battery combination - Invalid combination of different battery types."); + put(80, "Cutting system imbalance Warning"); + put(81, "Safety function faulty"); + put(82, "Wheel motor blocked, rear right"); + put(83, "Wheel motor blocked, rear left"); + put(84, "Wheel drive problem, rear right"); + put(85, "Wheel drive problem, rear left"); + put(86, "Wheel motor overloaded, rear right"); + put(87, "Wheel motor overloaded, rear left"); + put(88, "Angular sensor problem"); + put(89, "Invalid system configuration"); + put(90, "No power in charging station"); + put(91, "Switch cord problem"); + put(92, "Work area not valid"); + put(93, "No accurate position from satellites"); + put(94, "Reference station communication problem"); + put(95, "Folding sensor activated"); + put(96, "Right brush motor overloaded"); + put(97, "Left brush motor overloaded"); + put(98, "Ultrasonic Sensor 1 defect"); + put(99, "Ultrasonic Sensor 2 defect"); + put(100, "Ultrasonic Sensor 3 defect"); + put(101, "Ultrasonic Sensor 4 defect"); + put(102, "Cutting drive motor 1 defect"); + put(103, "Cutting drive motor 2 defect"); + put(104, "Cutting drive motor 3 defect"); + put(105, "Lift Sensor defect"); + put(106, "Collision sensor defect"); + put(107, "Docking sensor defect"); + put(108, "Folding cutting deck sensor defect"); + put(109, "Loop sensor defect"); + put(110, "Collision sensor error"); + put(111, "No confirmed position"); + put(112, "Cutting system major imbalance"); + put(113, "Complex working area"); + put(114, "Too high discharge current"); + put(115, "Too high internal current"); + put(116, "High charging power loss"); + put(117, "High internal power loss"); + put(118, "Charging system problem"); + put(119, "Zone generator problem"); + put(120, "Internal voltage error"); + put(121, "High internal temerature"); + put(122, "CAN error"); + put(123, "Destination not reachable"); + put(124, "Destination blocked"); + put(125, "Battery needs replacement"); + put(126, "Battery near end of life"); + put(127, "Battery problem"); + put(128, "Multiple reference stations detected"); + put(129, "Auxiliary cutting means blocked"); + put(130, "Imbalanced auxiliary cutting disc detected"); + put(131, "Lifted in link arm"); + put(132, "EPOS accessory missing"); + put(133, "Bluetooth com with CS failed"); + put(134, "Invalid SW configuration"); + put(135, "Radar problem"); + put(136, "Work area tampered"); + put(137, "High temperature in cutting motor, right"); + put(138, "High temperature in cutting motor, center"); + put(139, "High temperature in cutting motor, left"); + put(141, "Wheel brush motor problem"); + put(143, "Accessory power problem"); + put(144, "Boundary wire problem"); + put(701, "Connectivity problem"); + put(702, "Connectivity settings restored"); + put(703, "Connectivity problem"); + put(704, "Connectivity problem"); + put(705, "Connectivity problem"); + put(706, "Poor signal quality"); + put(707, "SIM card requires PIN"); + put(708, "SIM card locked"); + put(709, "SIM card not found"); + put(710, "SIM card locked"); + put(711, "SIM card locked"); + put(712, "SIM card locked"); + put(713, "Geofence problem"); + put(714, "Geofence problem"); + put(715, "Connectivity problem"); + put(716, "Connectivity problem"); + put(717, "SMS could not be sent"); + put(724, "Communication circuit board SW must be updated"); + } + }; } diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index fd647cc08e07e..d1c3650c5730c 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -167,7 +167,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { sendAutomowerResetCuttingBladeUsageTime(); } } else if (command instanceof QuantityType cmd) { - if (cmd.toBigDecimal().intValue() == 0) { + if (cmd.intValue() == 0) { sendAutomowerResetCuttingBladeUsageTime(); } } @@ -710,6 +710,10 @@ private String restrictedState(RestrictedReason reason) { return "RESTRICTED_" + reason.name(); } + private @Nullable String getErrorMessage(int errorCode) { + return ERROR.get(errorCode); + } + private @Nullable WorkArea getWorkAreaById(@Nullable Mower mower, long workAreaId) { if (mower != null) { List workAreas = mower.getAttributes().getWorkAreas(); @@ -760,7 +764,14 @@ private void updateChannelState(@Nullable Mower mower) { updateState(CHANNEL_STATUS_BATTERY, new QuantityType<>(mower.getAttributes().getBattery().getBatteryPercent(), Units.PERCENT)); - updateState(CHANNEL_STATUS_ERROR_CODE, new DecimalType(mower.getAttributes().getMower().getErrorCode())); + int errorCode = mower.getAttributes().getMower().getErrorCode(); + updateState(CHANNEL_STATUS_ERROR_CODE, new DecimalType(errorCode)); + String errorMessage = getErrorMessage(errorCode); + if (errorMessage != null) { + updateState(CHANNEL_STATUS_ERROR_MESSAGE, new StringType(errorMessage)); + } else { + updateState(CHANNEL_STATUS_ERROR_MESSAGE, UnDefType.NULL); + } long errorCodeTimestamp = mower.getAttributes().getMower().getErrorCodeTimestamp(); if (errorCodeTimestamp == 0L) { diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties index c34042d02d247..50a343a4fff17 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties @@ -65,6 +65,8 @@ channel-type.automower.errorCodeType.label = Error Code channel-type.automower.errorCodeType.description = The error code at the time of last update channel-type.automower.errorConfirmableType.label = Error Confirmable channel-type.automower.errorConfirmableType.description = If the mower has an Error Code this attribute states if the error is confirmable +channel-type.automower.errorMessageType.label = Error Message +channel-type.automower.errorMessageType.description = The error message at the time of last update channel-type.automower.errorTimestampType.label = Error Time channel-type.automower.errorTimestampType.description = The time when the error occurred channel-type.automower.inactiveReasonType.label = Inactive Reason diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 5504b7f40da8c..825d3043f0a46 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -54,6 +54,7 @@ + @@ -364,6 +365,7 @@ N/A N/A N/A + 1 @@ -466,7 +468,7 @@
- + Number Id of the active work area @@ -494,12 +496,18 @@ - + Number The error code at the time of last update + + String + + The error message at the time of last update + + DateTime @@ -507,7 +515,7 @@ - + Switch If the mower has an Error Code this attribute states if the error is confirmable @@ -529,7 +537,7 @@ - + String The channel providing a reason that restrics current planner operation. @@ -548,7 +556,7 @@
- + Number The channel providing an external reason that restrics current planner operation. @@ -574,7 +582,7 @@ - + Number:Time The time the mower has been disconnected from the cloud @@ -644,7 +652,7 @@ - + Number:Time The time the mower has been connected to the cloud @@ -666,54 +674,54 @@ - + Number:Time Start time relative to midnight - + Number:Time Duration time - + Switch Enabled on Mondays - + Switch Enabled on Tuesdays - + Switch Enabled on Wednesdays - + Switch Enabled on Thursdays - + Switch Enabled on Fridays - + Switch Enabled on Saturdays - + Switch Enabled on Sundays - + Number Work Area Id mapped to this calendar @@ -729,49 +737,49 @@ - + String Id of the Stayout zone - + String The name of the Stayout zone - + Switch If the Stayout zone is enabled, the Automower® will not access the zone - + Number Id of the Work Area - + String Name of the work area - + Number:Dimensionless Cutting height in percent. 0-100 - + Switch If the work area is enabled or disabled From 55def926b3b8fd3081c3e670a4d41b87aafcb19d Mon Sep 17 00:00:00 2001 From: Michael Weger Date: Sat, 23 Nov 2024 14:21:22 +0100 Subject: [PATCH 35/35] static channels + channel groups Signed-off-by: Michael Weger --- .../org.openhab.binding.automower/README.md | 198 ++-- .../internal/AutomowerBindingConstants.java | 288 +++--- .../internal/things/AutomowerCommand.java | 14 +- .../internal/things/AutomowerHandler.java | 19 +- .../OH-INF/i18n/automower.properties | 88 +- .../resources/OH-INF/thing/thing-types.xml | 705 +++++++------ .../main/resources/OH-INF/update/update.xml | 973 ++++++++++++++++++ 7 files changed, 1663 insertions(+), 622 deletions(-) create mode 100644 bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml diff --git a/bundles/org.openhab.binding.automower/README.md b/bundles/org.openhab.binding.automower/README.md index 3ce58fae16285..0bedccecf7be8 100644 --- a/bundles/org.openhab.binding.automower/README.md +++ b/bundles/org.openhab.binding.automower/README.md @@ -40,57 +40,68 @@ With the default value of 10min this would mean ~4300 requests per month per sin ### Status Channels -| channel | type | access mode | description | advanced | -|------------|----------|-------------|--------------|----------| -| name | String | R | The name of the Automower® | false | -| mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | false | -| activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | false | -| inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | false | -| state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | false | -| work-area-id | Number | R | Id of the active work area | true | -| work-area | String | R | Name of the active work area | false | -| last-update | DateTime | R | The time when the Automower® updated its states | false | -| battery | Number:Dimensionless | R | The battery state of charge in percent | false | -| error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | true | -| error-message | String | R | The current error message | false | -| error-timestamp | DateTime | R | The timestamp when the current error occurred | false | -| error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | true | -| planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | false | -| planner-override-action | String | R | The action that overrides current planner operation | true | -| planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | false | -| planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | true | -| setting-cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | false | -| setting-headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | false | -| stat-cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | false | -| stat-down-time | Number:Time | R | The time the mower has been disconnected from the cloud | true | -| stat-number-of-charging-cycles | Number | R | Number of charging cycles | false | -| stat-number-of-collisions | Number | R | The total number of collisions | false | -| stat-total-charging-time | Number:Time | R | Total charging time | false | -| stat-total-cutting-time | Number:Time | R | Total Cutting Time | false | -| stat-total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | false | -| stat-total-drive-distance | Number:Length | R | Total driven distance | false | -| stat-total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | false | -| stat-total-searching-time | Number:Length | R | The total searching time | false | -| stat-total-searching-percent | Number:Dimensionless | R | The total searching time in percent | false | -| stat-up-time | Number:Time | R | The time the mower has been connected to the cloud | true | +| channel | type | access mode | description | advanced | +|---------------------------------------|----------------------|-----|----------------------------------------------------------------------------------------------------------------|-------| +| status#name | String | R | The name of the Automower® | false | +| status#mode | String | R | The current mode (MAIN_AREA, SECONDARY_AREA, HOME, DEMO, UNKNOWN) | false | +| status#activity | String | R | The current activity (UNKNOWN, NOT_APPLICABLE, MOWING, GOING_HOME, CHARGING, LEAVING, PARKED_IN_CS, STOPPED_IN_GARDEN) | false | +| status#inactive-reason | String | R | The current reason for being inactive (NONE, PLANNING, SEARCHING_FOR_SATELLITES) | false | +| status#state | String | R | The current state (UNKNOWN, NOT_APPLICABLE, PAUSED, IN_OPERATION, WAIT_UPDATING, WAIT_POWER_UP, RESTRICTED_NONE, RESTRICTED_WEEK_SCHEDULE, RESTRICTED_PARK_OVERRIDE, RESTRICTED_SENSOR, RESTRICTED_DAILY_LIMIT, RESTRICTED_FOTA, RESTRICTED_FROST, RESTRICTED_ALL_WORK_AREAS_COMPLETED, RESTRICTED_EXTERNAL, OFF, STOPPED, ERROR, FATAL_ERROR, ERROR_AT_POWER_UP) | false | +| status#work-area-id | Number | R | Id of the active work area | true | +| status#work-area | String | R | Name of the active work area | false | +| status#last-update | DateTime | R | The time when the Automower® updated its states | false | +| status#battery | Number:Dimensionless | R | The battery state of charge in percent | false | +| status#error-code | Number | R/W | The current error code. `sendCommand(0)` to confirm current non fatal error | true | +| status#error-message | String | R | The current error message | false | +| status#error-timestamp | DateTime | R | The timestamp when the current error occurred | false | +| status#error-confirmable | Switch | R | If the mower has an Error Code this attribute states if the error is confirmable | true | +| status#planner-next-start | DateTime | R | The time for the next auto start. If the mower is charging then the value is the estimated time when it will be leaving the charging station. If the mower is about to start now, the value is NULL | false | +| status#planner-override-action | String | R | The action that overrides current planner operation | true | +| status#planner-restricted-reason | String | R | A reason that restrics current planner operation (NONE, WEEK_SCHEDULE, PARK_OVERRIDE, SENSOR, DAILY_LIMIT, FOTA, FROST, ALL_WORK_AREAS_COMPLETED, EXTERNAL) | false | +| status#planner-external-reason | String | R | An external reason set by i.e. IFTTT, Google Assistant or Amazon Alexa that restrics current planner operation | true | + + +### Settings Channels + +| channel | type | access mode | description | advanced | +|------------------------|--------|-------------|-------------------------------------------------------------------------|----------| +| setting#cutting-height | Number | R/W | Prescaled cutting height, Range: 1-9 | false | +| setting#headlight-mode | String | R/W | Headlight Mode (ALWAYS_ON, ALWAYS_OFF, EVENING_ONLY, EVENING_AND_NIGHT) | false | + +### Statistics Channels + +| channel | type | access mode | description | advanced | +|-------------------------------------|----------------------|-------------|---------------------------------------------------------------------------------------------|----------| +| statistic#cutting-blade-usage-time | Number:Time | R/W | The time since the last reset of the cutting blade usage counter. `sendCommand(0)` to reset | false | +| statistic#down-time | Number:Time | R | The time the mower has been disconnected from the cloud | true | +| statistic#number-of-charging-cycles | Number | R | Number of charging cycles | false | +| statistic#number-of-collisions | Number | R | The total number of collisions | false | +| statistic#total-charging-time | Number:Time | R | Total charging time | false | +| statistic#total-cutting-time | Number:Time | R | Total Cutting Time | false | +| statistic#total-cutting-percent | Number:Dimensionless | R | Total cutting time in percent | false | +| statistic#total-drive-distance | Number:Length | R | Total driven distance | false | +| statistic#total-running-time | Number:Time | R | The total running time (the wheel motors have been running) | false | +| statistic#total-searching-time | Number:Length | R | The total searching time | false | +| statistic#total-searching-percent | Number:Dimensionless | R | The total searching time in percent | false | +| statistic#up-time | Number:Time | R | The time the mower has been connected to the cloud | true | ### Calendar Tasks Channels These channels hold the different Calendar Task configurations. Right now a maximum of 10 Calendar Tasks are supported by the binding. -| channel | type | access mode | description | advanced | -|------------|----------|-------------|--------------|----------| -| calendartasks\-start | Number:Time | R/W | Start time relative to midnight | true | -| calendartasks\-duration | Number:Time | R/W | Duration time | true | -| calendartasks\-monday | Switch | R/W | Enabled on Mondays | true | -| calendartasks\-tuesday | Switch | R/W | Enabled on Tuesdays | true | -| calendartasks\-wednesday | Switch | R/W | Enabled on Wednesdays | true | -| calendartasks\-thursday | Switch | R/W | Enabled on Thursdays | true | -| calendartasks\-friday | Switch | R/W | Enabled on Fridays | true | -| calendartasks\-saturday | Switch | R/W | Enabled on Saturdays | true | -| calendartasks\-sunday | Switch | R/W | Enabled on Sundays | true | -| calendartasks\-workAreaId | Number | R | Work Area Id mapped to this calendar | true | -| calendartasks\-workArea | String | R | Name of the Work Area mapped to this calendar | true | +| channel | type | access mode | description | advanced | +|-------------------------------|-------------|-------------|-----------------------------------------------|----------| +| calendartask#\-start | Number:Time | R/W | Start time relative to midnight | true | +| calendartask#\-duration | Number:Time | R/W | Duration time | true | +| calendartask#\-monday | Switch | R/W | Enabled on Mondays | true | +| calendartask#\-tuesday | Switch | R/W | Enabled on Tuesdays | true | +| calendartask#\-wednesday | Switch | R/W | Enabled on Wednesdays | true | +| calendartask#\-thursday | Switch | R/W | Enabled on Thursdays | true | +| calendartask#\-friday | Switch | R/W | Enabled on Fridays | true | +| calendartask#\-saturday | Switch | R/W | Enabled on Saturdays | true | +| calendartask#\-sunday | Switch | R/W | Enabled on Sundays | true | +| calendartask#\-workAreaId | Number | R | Work Area Id mapped to this calendar | true | +| calendartask#\-workArea | String | R | Name of the Work Area mapped to this calendar | true | \ ... 01-10 @@ -100,10 +111,10 @@ These channels hold the last 50 GPS positions recorded by the Automower®, thus Position 01 is the latest recorded position, the other positions are pushed back, thus removing the previous position 50 from the list because it is replaced by the previous position 49. Channel `last-position` is always identical with channel `position01` and thus provides more convenient access if only the latest GPS information is required by the user. -| channel | type | access mode | description | advanced | -|------------|----------|-------------|--------------|----------| -| last-position | Location | R | Last GPS Position (identical with positions#position01) | false | -| position\ | Location | R | GPS Position \ | true | +| channel | type | access mode | description | advanced | +|------------------|----------|-------------|---------------------------------------------------------|----------| +| position#last | Location | R | Last GPS Position (identical with positions#position01) | false | +| position#\ | Location | R | GPS Position \ | true | \ ... 01-50 @@ -111,12 +122,12 @@ Channel `last-position` is always identical with channel `position01` and thus p These channels hold the different Stayout Zone configurations. Right now a maximum of 10 Stayout Zones are supported by the binding. -| channel | type | access mode | description | advanced | -|------------|----------|-------------|--------------|----------| -| dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | -| zone\-id | String | R | Id of the Stayout zone | true | -| zone\-name | String | R | The name of the Stayout zone | true | -| zone\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | true | +| channel | type | access mode | description | advanced | +|---------------------------|--------|-------------|------------------------------------------------------------------------------------------------------------------------------------|----------| +| stayoutzone#dirty | Switch | R | If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone | true | +| stayoutzone#\-id | String | R | Id of the Stayout zone | true | +| stayoutzone#\-name | String | R | The name of the Stayout zone | true | +| stayoutzone#\-enabled | Switch | R/W | If the Stayout zone is enabled, the Automower® will not access the zone | true | \ ... 01-10 @@ -124,27 +135,27 @@ These channels hold the different Stayout Zone configurations. Right now a maxim These channels hold the different Work Area configurations. Right now a maximum of 10 Work Areas are supported by the binding. -| channel | type | access mode | description | advanced | -|------------|----------|-------------|--------------|----------| -| workarea\-id | Number | R | Id of the Work Area | false | -| workarea\-name | String | R | Name of the work area | false | -| workarea\-cutting-height | Number:Dimensionless | R/W | Cutting height in percent. 0-100 | false | -| workarea\-enabled | Switch | R/W | If the work area is enabled or disabled | false | -| workarea\-progress | Number | R | The progress on a work area. EPOS mowers and systematic mowing work areas only | true | -| workarea\-last-time-completed | DateTime | R | Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only | true | +| channel | type | access mode | description | advanced | +|------------------------------------|-----------------------|-------------|----------------------------------------------------------------------------------------------------|----------| +| workarea#\-id | Number | R | Id of the Work Area | false | +| workarea#\-name | String | R | Name of the work area | false | +| workarea#\-cutting-height | Number:Dimensionless | R/W | Cutting height in percent. 0-100 | false | +| workarea#\-enabled | Switch | R/W | If the work area is enabled or disabled | false | +| workarea#\-progress | Number | R | The progress on a work area. EPOS mowers and systematic mowing work areas only | true | +| workarea#\-last-time-completed | DateTime | R | Timestamp when the work area was last completed. EPOS mowers and systematic mowing work areas only | true | \ ... 01-10 ### Command Channels -| channel | type | access mode | description | advanced | -|---------------------------|----------|-------------|------------------------------------------------|----------| -| start | Number | W | Starts the Automower® for a duration | false | -| resume_schedule | Switch | W | Resumes the Automower® schedule | false | -| pause | Switch | W | Pause the Automower® | false | -| park | Number | W | Park the Automower® for a duration | false | -| park_until_next_schedule | Switch | W | Park the Automower® until next schedule | false | -| park_until_further_notice | Switch | W | Park the Automower® until further notice | false | +| channel | type | access mode | description | advanced | +|-----------------------------------|----------|-------------|------------------------------------------|----------| +| command#start | Number | W | Starts the Automower® for a duration | false | +| command#resume_schedule | Switch | W | Resumes the Automower® schedule | false | +| command#pause | Switch | W | Pause the Automower® | false | +| command#park | Number | W | Park the Automower® for a duration | false | +| command#park_until_next_schedule | Switch | W | Park the Automower® until next schedule | false | +| command#park_until_further_notice | Switch | W | Park the Automower® until further notice | false | ## Actions @@ -171,7 +182,7 @@ The following actions are available for `automower` things: ```java Bridge automower:bridge:mybridge [ appKey="", userName="", password="" ] { - Thing automower myAutomower [ mowerId="", pollingInterval=3600] { + Thing automower myAutomower [ mowerId="", pollingInterval=3600 ] { } } ``` @@ -179,25 +190,24 @@ Bridge automower:bridge:mybridge [ appKey="", user ### automower.items ```java -String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:mode" } -String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:activity" } -String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:state" } -DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:last-update" } -Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:battery" } -Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:error-code" } -DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:error-timestamp" } -String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:planner-override-action" } -DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:planner-next-start" } -String Automower_Calendar_Tasks "Planned Tasks [%s]" { channel="automower:automower:mybridge:myAutomower:calendar-tasks" } - -Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:start" } -Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:resume_schedule" } -Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:pause" } -Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:park" } -Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:park_until_next_schedule" } -Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:park_until_further_notice" } - -Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:last-position" } +String Automower_Mode "Mode [%s]" { channel="automower:automower:mybridge:myAutomower:status#mode" } +String Automower_Activity "Activity [%s]" { channel="automower:automower:mybridge:myAutomower:status#activity" } +String Automower_State "State [%s]" { channel="automower:automower:mybridge:myAutomower:status#state" } +DateTime Automower_Last_Update "Last Update" { channel="automower:automower:mybridge:myAutomower:status#last-update" } +Number Automower_Battery "Battery [%d %%]" { channel="automower:automower:mybridge:myAutomower:status#battery" } +Number Automower_Error_Code "Error Code [%d]" { channel="automower:automower:mybridge:myAutomower:status#error-code" } +DateTime Automower_Error_Time "Error Time" { channel="automower:automower:mybridge:myAutomower:status#error-timestamp" } +String Automower_Override_Action "Override Action [%s]" { channel="automower:automower:mybridge:myAutomower:status#planner-override-action" } +DateTime Automower_Next_Start_Time "Next Start Time" { channel="automower:automower:mybridge:myAutomower:status#planner-next-start" } + +Number Automower_Command_Start "Start mowing for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:command#start" } +Switch Automower_Command_Resume "Resume the schedule" { channel="automower:automower:mybridge:myAutomower:command#resume_schedule" } +Switch Automower_Command_Pause "Pause the automower" { channel="automower:automower:mybridge:myAutomower:command#pause" } +Number Automower_Command_Park "Park for duration [%d min]" { channel="automower:automower:mybridge:myAutomower:command#park" } +Switch Automower_Command_Park_Next_Schedule "Park until next schedule" { channel="automower:automower:mybridge:myAutomower:command#park_until_next_schedule" } +Switch Automower_Command_Park_Notice "Park until further notice" { channel="automower:automower:mybridge:myAutomower:command#park_until_further_notice" } + +Location Automower_Last_Position "Last Position" { channel="automower:automower:mybridge:myAutomower:position#last-position" } ``` ### automower.sitemap @@ -229,6 +239,10 @@ rule "AutomowerParkUntilFurtherNotice" when Item Some_Item changed to ON then + // via command item + Automower_Command_Park_Notice.sendCommand(ON) + + // alternative via actions val mowerActions = getActions("automower", "automower:automower:mybridge:myAutomower") mowerActions.parkUntilFurtherNotice() end diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java index cdd354a8c11cd..f12d253d1c081 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/AutomowerBindingConstants.java @@ -37,8 +37,8 @@ public class AutomowerBindingConstants { public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "automower"); // List of all status Channel ids - public static final String GROUP_STATUS = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release + public static final String GROUP_STATUS = "status#"; + public static final String CHANNEL_STATUS_NAME = GROUP_STATUS + "name"; public static final String CHANNEL_STATUS_MODE = GROUP_STATUS + "mode"; public static final String CHANNEL_STATUS_ACTIVITY = GROUP_STATUS + "activity"; @@ -52,193 +52,155 @@ public class AutomowerBindingConstants { public static final String CHANNEL_STATUS_ERROR_MESSAGE = GROUP_STATUS + "error-message"; public static final String CHANNEL_STATUS_ERROR_TIMESTAMP = GROUP_STATUS + "error-timestamp"; public static final String CHANNEL_STATUS_ERROR_CONFIRMABLE = GROUP_STATUS + "error-confirmable"; - - // List of all planner Channel ids - public static final String GROUP_PLANNER = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release - public static final String CHANNEL_PLANNER_NEXT_START = GROUP_PLANNER + "planner-next-start"; - public static final String CHANNEL_PLANNER_OVERRIDE_ACTION = GROUP_PLANNER + "planner-override-action"; - public static final String CHANNEL_PLANNER_RESTRICTED_REASON = GROUP_PLANNER + "planner-restricted-reason"; - public static final String CHANNEL_PLANNER_EXTERNAL_REASON = GROUP_PLANNER + "planner-external-reason"; + public static final String CHANNEL_STATUS_NEXT_START = GROUP_STATUS + "next-start"; + public static final String CHANNEL_STATUS_OVERRIDE_ACTION = GROUP_STATUS + "override-action"; + public static final String CHANNEL_STATUS_RESTRICTED_REASON = GROUP_STATUS + "restricted-reason"; + public static final String CHANNEL_STATUS_EXTERNAL_REASON = GROUP_STATUS + "external-reason"; // List of all setting Channel ids - public static final String GROUP_SETTING = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release - public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_SETTING + "setting-cutting-height"; - public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_SETTING + "setting-headlight-mode"; + public static final String GROUP_SETTING = "setting#"; + + public static final String CHANNEL_SETTING_CUTTING_HEIGHT = GROUP_SETTING + "cutting-height"; + public static final String CHANNEL_SETTING_HEADLIGHT_MODE = GROUP_SETTING + "headlight-mode"; // List of all setting Channel ids - public static final String GROUP_STATISTIC = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release + public static final String GROUP_STATISTIC = "statistic#"; + public static final String CHANNEL_STATISTIC_CUTTING_BLADE_USAGE_TIME = GROUP_STATISTIC - + "stat-cutting-blade-usage-time"; - public static final String CHANNEL_STATISTIC_DOWN_TIME = GROUP_STATISTIC + "stat-down-time"; + + "cutting-blade-usage-time"; + public static final String CHANNEL_STATISTIC_DOWN_TIME = GROUP_STATISTIC + "down-time"; public static final String CHANNEL_STATISTIC_NUMBER_OF_CHARGING_CYCLES = GROUP_STATISTIC - + "stat-number-of-charging-cycles"; - public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATISTIC + "stat-number-of-collisions"; - public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATISTIC + "stat-total-charging-time"; - public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATISTIC + "stat-total-cutting-time"; - public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATISTIC + "stat-total-cutting-percent"; - public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATISTIC + "stat-total-drive-distance"; - public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATISTIC + "stat-total-running-time"; - public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATISTIC + "stat-total-searching-time"; - public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATISTIC - + "stat-total-searching-percent"; - public static final String CHANNEL_STATISTIC_UP_TIME = GROUP_STATISTIC + "stat-up-time"; + + "number-of-charging-cycles"; + public static final String CHANNEL_STATISTIC_NUMBER_OF_COLLISIONS = GROUP_STATISTIC + "number-of-collisions"; + public static final String CHANNEL_STATISTIC_TOTAL_CHARGING_TIME = GROUP_STATISTIC + "total-charging-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_TIME = GROUP_STATISTIC + "total-cutting-time"; + public static final String CHANNEL_STATISTIC_TOTAL_CUTTING_PERCENT = GROUP_STATISTIC + "total-cutting-percent"; + public static final String CHANNEL_STATISTIC_TOTAL_DRIVE_DISTANCE = GROUP_STATISTIC + "total-drive-distance"; + public static final String CHANNEL_STATISTIC_TOTAL_RUNNING_TIME = GROUP_STATISTIC + "total-running-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_TIME = GROUP_STATISTIC + "total-searching-time"; + public static final String CHANNEL_STATISTIC_TOTAL_SEARCHING_PERCENT = GROUP_STATISTIC + "total-searching-percent"; + public static final String CHANNEL_STATISTIC_UP_TIME = GROUP_STATISTIC + "up-time"; // Calendar Task Channels ids - public static final String GROUP_CALENDARTASKS = ""; // no channel group in use at the moment, we'll possibly - // introduce this in a future release + public static final String GROUP_CALENDARTASK = "calendartask#"; + public static final ArrayList CHANNEL_CALENDARTASKS = new ArrayList<>(List.of( - GROUP_CALENDARTASKS + "calendartasks01-start", GROUP_CALENDARTASKS + "calendartasks01-duration", - GROUP_CALENDARTASKS + "calendartasks01-monday", GROUP_CALENDARTASKS + "calendartasks01-tuesday", - GROUP_CALENDARTASKS + "calendartasks01-wednesday", GROUP_CALENDARTASKS + "calendartasks01-thursday", - GROUP_CALENDARTASKS + "calendartasks01-friday", GROUP_CALENDARTASKS + "calendartasks01-saturday", - GROUP_CALENDARTASKS + "calendartasks01-sunday", GROUP_CALENDARTASKS + "calendartasks01-workAreaId", - GROUP_CALENDARTASKS + "calendartasks01-workArea", GROUP_CALENDARTASKS + "calendartasks02-start", - GROUP_CALENDARTASKS + "calendartasks02-duration", GROUP_CALENDARTASKS + "calendartasks02-monday", - GROUP_CALENDARTASKS + "calendartasks02-tuesday", GROUP_CALENDARTASKS + "calendartasks02-wednesday", - GROUP_CALENDARTASKS + "calendartasks02-thursday", GROUP_CALENDARTASKS + "calendartasks02-friday", - GROUP_CALENDARTASKS + "calendartasks02-saturday", GROUP_CALENDARTASKS + "calendartasks02-sunday", - GROUP_CALENDARTASKS + "calendartasks02-workAreaId", GROUP_CALENDARTASKS + "calendartasks02-workArea", - GROUP_CALENDARTASKS + "calendartasks03-start", GROUP_CALENDARTASKS + "calendartasks03-duration", - GROUP_CALENDARTASKS + "calendartasks03-monday", GROUP_CALENDARTASKS + "calendartasks03-tuesday", - GROUP_CALENDARTASKS + "calendartasks03-wednesday", GROUP_CALENDARTASKS + "calendartasks03-thursday", - GROUP_CALENDARTASKS + "calendartasks03-friday", GROUP_CALENDARTASKS + "calendartasks03-saturday", - GROUP_CALENDARTASKS + "calendartasks03-sunday", GROUP_CALENDARTASKS + "calendartasks03-workAreaId", - GROUP_CALENDARTASKS + "calendartasks03-workArea", GROUP_CALENDARTASKS + "calendartasks04-start", - GROUP_CALENDARTASKS + "calendartasks04-duration", GROUP_CALENDARTASKS + "calendartasks04-monday", - GROUP_CALENDARTASKS + "calendartasks04-tuesday", GROUP_CALENDARTASKS + "calendartasks04-wednesday", - GROUP_CALENDARTASKS + "calendartasks04-thursday", GROUP_CALENDARTASKS + "calendartasks04-friday", - GROUP_CALENDARTASKS + "calendartasks04-saturday", GROUP_CALENDARTASKS + "calendartasks04-sunday", - GROUP_CALENDARTASKS + "calendartasks04-workAreaId", GROUP_CALENDARTASKS + "calendartasks04-workArea", - GROUP_CALENDARTASKS + "calendartasks05-start", GROUP_CALENDARTASKS + "calendartasks05-duration", - GROUP_CALENDARTASKS + "calendartasks05-monday", GROUP_CALENDARTASKS + "calendartasks05-tuesday", - GROUP_CALENDARTASKS + "calendartasks05-wednesday", GROUP_CALENDARTASKS + "calendartasks05-thursday", - GROUP_CALENDARTASKS + "calendartasks05-friday", GROUP_CALENDARTASKS + "calendartasks05-saturday", - GROUP_CALENDARTASKS + "calendartasks05-sunday", GROUP_CALENDARTASKS + "calendartasks05-workAreaId", - GROUP_CALENDARTASKS + "calendartasks05-workArea", GROUP_CALENDARTASKS + "calendartasks06-start", - GROUP_CALENDARTASKS + "calendartasks06-duration", GROUP_CALENDARTASKS + "calendartasks06-monday", - GROUP_CALENDARTASKS + "calendartasks06-tuesday", GROUP_CALENDARTASKS + "calendartasks06-wednesday", - GROUP_CALENDARTASKS + "calendartasks06-thursday", GROUP_CALENDARTASKS + "calendartasks06-friday", - GROUP_CALENDARTASKS + "calendartasks06-saturday", GROUP_CALENDARTASKS + "calendartasks06-sunday", - GROUP_CALENDARTASKS + "calendartasks06-workAreaId", GROUP_CALENDARTASKS + "calendartasks06-workArea", - GROUP_CALENDARTASKS + "calendartasks07-start", GROUP_CALENDARTASKS + "calendartasks07-duration", - GROUP_CALENDARTASKS + "calendartasks07-monday", GROUP_CALENDARTASKS + "calendartasks07-tuesday", - GROUP_CALENDARTASKS + "calendartasks07-wednesday", GROUP_CALENDARTASKS + "calendartasks07-thursday", - GROUP_CALENDARTASKS + "calendartasks07-friday", GROUP_CALENDARTASKS + "calendartasks07-saturday", - GROUP_CALENDARTASKS + "calendartasks07-sunday", GROUP_CALENDARTASKS + "calendartasks07-workAreaId", - GROUP_CALENDARTASKS + "calendartasks07-workArea", GROUP_CALENDARTASKS + "calendartasks08-start", - GROUP_CALENDARTASKS + "calendartasks08-duration", GROUP_CALENDARTASKS + "calendartasks08-monday", - GROUP_CALENDARTASKS + "calendartasks08-tuesday", GROUP_CALENDARTASKS + "calendartasks08-wednesday", - GROUP_CALENDARTASKS + "calendartasks08-thursday", GROUP_CALENDARTASKS + "calendartasks08-friday", - GROUP_CALENDARTASKS + "calendartasks08-saturday", GROUP_CALENDARTASKS + "calendartasks08-sunday", - GROUP_CALENDARTASKS + "calendartasks08-workAreaId", GROUP_CALENDARTASKS + "calendartasks08-workArea", - GROUP_CALENDARTASKS + "calendartasks09-start", GROUP_CALENDARTASKS + "calendartasks09-duration", - GROUP_CALENDARTASKS + "calendartasks09-monday", GROUP_CALENDARTASKS + "calendartasks09-tuesday", - GROUP_CALENDARTASKS + "calendartasks09-wednesday", GROUP_CALENDARTASKS + "calendartasks09-thursday", - GROUP_CALENDARTASKS + "calendartasks09-friday", GROUP_CALENDARTASKS + "calendartasks09-saturday", - GROUP_CALENDARTASKS + "calendartasks09-sunday", GROUP_CALENDARTASKS + "calendartasks09-workAreaId", - GROUP_CALENDARTASKS + "calendartasks09-workArea", GROUP_CALENDARTASKS + "calendartasks10-start", - GROUP_CALENDARTASKS + "calendartasks10-duration", GROUP_CALENDARTASKS + "calendartasks10-monday", - GROUP_CALENDARTASKS + "calendartasks10-tuesday", GROUP_CALENDARTASKS + "calendartasks10-wednesday", - GROUP_CALENDARTASKS + "calendartasks10-thursday", GROUP_CALENDARTASKS + "calendartasks10-friday", - GROUP_CALENDARTASKS + "calendartasks10-saturday", GROUP_CALENDARTASKS + "calendartasks10-sunday", - GROUP_CALENDARTASKS + "calendartasks10-workAreaId", GROUP_CALENDARTASKS + "calendartasks10-workArea")); + GROUP_CALENDARTASK + "01-start", GROUP_CALENDARTASK + "01-duration", GROUP_CALENDARTASK + "01-monday", + GROUP_CALENDARTASK + "01-tuesday", GROUP_CALENDARTASK + "01-wednesday", GROUP_CALENDARTASK + "01-thursday", + GROUP_CALENDARTASK + "01-friday", GROUP_CALENDARTASK + "01-saturday", GROUP_CALENDARTASK + "01-sunday", + GROUP_CALENDARTASK + "01-workAreaId", GROUP_CALENDARTASK + "01-workArea", GROUP_CALENDARTASK + "02-start", + GROUP_CALENDARTASK + "02-duration", GROUP_CALENDARTASK + "02-monday", GROUP_CALENDARTASK + "02-tuesday", + GROUP_CALENDARTASK + "02-wednesday", GROUP_CALENDARTASK + "02-thursday", GROUP_CALENDARTASK + "02-friday", + GROUP_CALENDARTASK + "02-saturday", GROUP_CALENDARTASK + "02-sunday", GROUP_CALENDARTASK + "02-workAreaId", + GROUP_CALENDARTASK + "02-workArea", GROUP_CALENDARTASK + "03-start", GROUP_CALENDARTASK + "03-duration", + GROUP_CALENDARTASK + "03-monday", GROUP_CALENDARTASK + "03-tuesday", GROUP_CALENDARTASK + "03-wednesday", + GROUP_CALENDARTASK + "03-thursday", GROUP_CALENDARTASK + "03-friday", GROUP_CALENDARTASK + "03-saturday", + GROUP_CALENDARTASK + "03-sunday", GROUP_CALENDARTASK + "03-workAreaId", GROUP_CALENDARTASK + "03-workArea", + GROUP_CALENDARTASK + "04-start", GROUP_CALENDARTASK + "04-duration", GROUP_CALENDARTASK + "04-monday", + GROUP_CALENDARTASK + "04-tuesday", GROUP_CALENDARTASK + "04-wednesday", GROUP_CALENDARTASK + "04-thursday", + GROUP_CALENDARTASK + "04-friday", GROUP_CALENDARTASK + "04-saturday", GROUP_CALENDARTASK + "04-sunday", + GROUP_CALENDARTASK + "04-workAreaId", GROUP_CALENDARTASK + "04-workArea", GROUP_CALENDARTASK + "05-start", + GROUP_CALENDARTASK + "05-duration", GROUP_CALENDARTASK + "05-monday", GROUP_CALENDARTASK + "05-tuesday", + GROUP_CALENDARTASK + "05-wednesday", GROUP_CALENDARTASK + "05-thursday", GROUP_CALENDARTASK + "05-friday", + GROUP_CALENDARTASK + "05-saturday", GROUP_CALENDARTASK + "05-sunday", GROUP_CALENDARTASK + "05-workAreaId", + GROUP_CALENDARTASK + "05-workArea", GROUP_CALENDARTASK + "06-start", GROUP_CALENDARTASK + "06-duration", + GROUP_CALENDARTASK + "06-monday", GROUP_CALENDARTASK + "06-tuesday", GROUP_CALENDARTASK + "06-wednesday", + GROUP_CALENDARTASK + "06-thursday", GROUP_CALENDARTASK + "06-friday", GROUP_CALENDARTASK + "06-saturday", + GROUP_CALENDARTASK + "06-sunday", GROUP_CALENDARTASK + "06-workAreaId", GROUP_CALENDARTASK + "06-workArea", + GROUP_CALENDARTASK + "07-start", GROUP_CALENDARTASK + "07-duration", GROUP_CALENDARTASK + "07-monday", + GROUP_CALENDARTASK + "07-tuesday", GROUP_CALENDARTASK + "07-wednesday", GROUP_CALENDARTASK + "07-thursday", + GROUP_CALENDARTASK + "07-friday", GROUP_CALENDARTASK + "07-saturday", GROUP_CALENDARTASK + "07-sunday", + GROUP_CALENDARTASK + "07-workAreaId", GROUP_CALENDARTASK + "07-workArea", GROUP_CALENDARTASK + "08-start", + GROUP_CALENDARTASK + "08-duration", GROUP_CALENDARTASK + "08-monday", GROUP_CALENDARTASK + "08-tuesday", + GROUP_CALENDARTASK + "08-wednesday", GROUP_CALENDARTASK + "08-thursday", GROUP_CALENDARTASK + "08-friday", + GROUP_CALENDARTASK + "08-saturday", GROUP_CALENDARTASK + "08-sunday", GROUP_CALENDARTASK + "08-workAreaId", + GROUP_CALENDARTASK + "08-workArea", GROUP_CALENDARTASK + "09-start", GROUP_CALENDARTASK + "09-duration", + GROUP_CALENDARTASK + "09-monday", GROUP_CALENDARTASK + "09-tuesday", GROUP_CALENDARTASK + "09-wednesday", + GROUP_CALENDARTASK + "09-thursday", GROUP_CALENDARTASK + "09-friday", GROUP_CALENDARTASK + "09-saturday", + GROUP_CALENDARTASK + "09-sunday", GROUP_CALENDARTASK + "09-workAreaId", GROUP_CALENDARTASK + "09-workArea", + GROUP_CALENDARTASK + "10-start", GROUP_CALENDARTASK + "10-duration", GROUP_CALENDARTASK + "10-monday", + GROUP_CALENDARTASK + "10-tuesday", GROUP_CALENDARTASK + "10-wednesday", GROUP_CALENDARTASK + "10-thursday", + GROUP_CALENDARTASK + "10-friday", GROUP_CALENDARTASK + "10-saturday", GROUP_CALENDARTASK + "10-sunday", + GROUP_CALENDARTASK + "10-workAreaId", GROUP_CALENDARTASK + "10-workArea")); // Position Channels ids - public static final String GROUP_POSITIONS = ""; // no channel group in use at the moment, we'll possibly - // introduce this in a future release - public static final String LAST_POSITION = GROUP_POSITIONS + "last-position"; + public static final String GROUP_POSITION = "position#"; + + public static final String LAST_POSITION = GROUP_POSITION + "last"; public static final ArrayList CHANNEL_POSITIONS = new ArrayList<>( - List.of(GROUP_POSITIONS + "position01", GROUP_POSITIONS + "position02", GROUP_POSITIONS + "position03", - GROUP_POSITIONS + "position04", GROUP_POSITIONS + "position05", GROUP_POSITIONS + "position06", - GROUP_POSITIONS + "position07", GROUP_POSITIONS + "position08", GROUP_POSITIONS + "position09", - GROUP_POSITIONS + "position10", GROUP_POSITIONS + "position11", GROUP_POSITIONS + "position12", - GROUP_POSITIONS + "position13", GROUP_POSITIONS + "position14", GROUP_POSITIONS + "position15", - GROUP_POSITIONS + "position16", GROUP_POSITIONS + "position17", GROUP_POSITIONS + "position18", - GROUP_POSITIONS + "position19", GROUP_POSITIONS + "position20", GROUP_POSITIONS + "position21", - GROUP_POSITIONS + "position22", GROUP_POSITIONS + "position23", GROUP_POSITIONS + "position24", - GROUP_POSITIONS + "position25", GROUP_POSITIONS + "position26", GROUP_POSITIONS + "position27", - GROUP_POSITIONS + "position28", GROUP_POSITIONS + "position29", GROUP_POSITIONS + "position30", - GROUP_POSITIONS + "position31", GROUP_POSITIONS + "position32", GROUP_POSITIONS + "position33", - GROUP_POSITIONS + "position34", GROUP_POSITIONS + "position35", GROUP_POSITIONS + "position36", - GROUP_POSITIONS + "position37", GROUP_POSITIONS + "position38", GROUP_POSITIONS + "position39", - GROUP_POSITIONS + "position40", GROUP_POSITIONS + "position41", GROUP_POSITIONS + "position42", - GROUP_POSITIONS + "position43", GROUP_POSITIONS + "position44", GROUP_POSITIONS + "position45", - GROUP_POSITIONS + "position46", GROUP_POSITIONS + "position47", GROUP_POSITIONS + "position48", - GROUP_POSITIONS + "position49", GROUP_POSITIONS + "position50")); + List.of(GROUP_POSITION + "01", GROUP_POSITION + "02", GROUP_POSITION + "03", GROUP_POSITION + "04", + GROUP_POSITION + "05", GROUP_POSITION + "06", GROUP_POSITION + "07", GROUP_POSITION + "08", + GROUP_POSITION + "09", GROUP_POSITION + "10", GROUP_POSITION + "11", GROUP_POSITION + "12", + GROUP_POSITION + "13", GROUP_POSITION + "14", GROUP_POSITION + "15", GROUP_POSITION + "16", + GROUP_POSITION + "17", GROUP_POSITION + "18", GROUP_POSITION + "19", GROUP_POSITION + "20", + GROUP_POSITION + "21", GROUP_POSITION + "22", GROUP_POSITION + "23", GROUP_POSITION + "24", + GROUP_POSITION + "25", GROUP_POSITION + "26", GROUP_POSITION + "27", GROUP_POSITION + "28", + GROUP_POSITION + "29", GROUP_POSITION + "30", GROUP_POSITION + "31", GROUP_POSITION + "32", + GROUP_POSITION + "33", GROUP_POSITION + "34", GROUP_POSITION + "35", GROUP_POSITION + "36", + GROUP_POSITION + "37", GROUP_POSITION + "38", GROUP_POSITION + "39", GROUP_POSITION + "40", + GROUP_POSITION + "41", GROUP_POSITION + "42", GROUP_POSITION + "43", GROUP_POSITION + "44", + GROUP_POSITION + "45", GROUP_POSITION + "46", GROUP_POSITION + "47", GROUP_POSITION + "48", + GROUP_POSITION + "49", GROUP_POSITION + "50")); // Stayout Zones Channels ids - public static final String GROUP_STAYOUTZONES = ""; // no channel group in use at the moment, we'll possibly - // introduce this in a future release - public static final String CHANNEL_STAYOUTZONES_DIRTY = GROUP_STAYOUTZONES + "dirty"; - public static final ArrayList CHANNEL_STAYOUTZONES = new ArrayList<>(List.of( - GROUP_STAYOUTZONES + "zone01-id", GROUP_STAYOUTZONES + "zone01-name", GROUP_STAYOUTZONES + "zone01-enabled", - GROUP_STAYOUTZONES + "zone02-id", GROUP_STAYOUTZONES + "zone02-name", GROUP_STAYOUTZONES + "zone02-enabled", - GROUP_STAYOUTZONES + "zone03-id", GROUP_STAYOUTZONES + "zone03-name", GROUP_STAYOUTZONES + "zone03-enabled", - GROUP_STAYOUTZONES + "zone04-id", GROUP_STAYOUTZONES + "zone04-name", GROUP_STAYOUTZONES + "zone04-enabled", - GROUP_STAYOUTZONES + "zone05-id", GROUP_STAYOUTZONES + "zone05-name", GROUP_STAYOUTZONES + "zone05-enabled", - GROUP_STAYOUTZONES + "zone06-id", GROUP_STAYOUTZONES + "zone06-name", GROUP_STAYOUTZONES + "zone06-enabled", - GROUP_STAYOUTZONES + "zone07-id", GROUP_STAYOUTZONES + "zone07-name", GROUP_STAYOUTZONES + "zone07-enabled", - GROUP_STAYOUTZONES + "zone08-id", GROUP_STAYOUTZONES + "zone08-name", GROUP_STAYOUTZONES + "zone08-enabled", - GROUP_STAYOUTZONES + "zone09-id", GROUP_STAYOUTZONES + "zone09-name", GROUP_STAYOUTZONES + "zone09-enabled", - GROUP_STAYOUTZONES + "zone10-id", GROUP_STAYOUTZONES + "zone10-name", - GROUP_STAYOUTZONES + "zone10-enabled")); + public static final String GROUP_STAYOUTZONE = "stayoutzone#"; + + public static final String CHANNEL_STAYOUTZONES_DIRTY = GROUP_STAYOUTZONE + "dirty"; + public static final ArrayList CHANNEL_STAYOUTZONES = new ArrayList<>( + List.of(GROUP_STAYOUTZONE + "01-id", GROUP_STAYOUTZONE + "01-name", GROUP_STAYOUTZONE + "01-enabled", + GROUP_STAYOUTZONE + "02-id", GROUP_STAYOUTZONE + "02-name", GROUP_STAYOUTZONE + "02-enabled", + GROUP_STAYOUTZONE + "03-id", GROUP_STAYOUTZONE + "03-name", GROUP_STAYOUTZONE + "03-enabled", + GROUP_STAYOUTZONE + "04-id", GROUP_STAYOUTZONE + "04-name", GROUP_STAYOUTZONE + "04-enabled", + GROUP_STAYOUTZONE + "05-id", GROUP_STAYOUTZONE + "05-name", GROUP_STAYOUTZONE + "05-enabled", + GROUP_STAYOUTZONE + "06-id", GROUP_STAYOUTZONE + "06-name", GROUP_STAYOUTZONE + "06-enabled", + GROUP_STAYOUTZONE + "07-id", GROUP_STAYOUTZONE + "07-name", GROUP_STAYOUTZONE + "07-enabled", + GROUP_STAYOUTZONE + "08-id", GROUP_STAYOUTZONE + "08-name", GROUP_STAYOUTZONE + "08-enabled", + GROUP_STAYOUTZONE + "09-id", GROUP_STAYOUTZONE + "09-name", GROUP_STAYOUTZONE + "09-enabled", + GROUP_STAYOUTZONE + "10-id", GROUP_STAYOUTZONE + "10-name", GROUP_STAYOUTZONE + "10-enabled")); // Work Areas Channels ids - public static final String GROUP_WORKAREAS = ""; // no channel group in use at the moment, we'll possibly - // introduce this in a future release - public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>( - List.of(GROUP_WORKAREAS + "workarea01-id", GROUP_WORKAREAS + "workarea01-name", - GROUP_WORKAREAS + "workarea01-cutting-height", GROUP_WORKAREAS + "workarea01-enabled", - GROUP_WORKAREAS + "workarea01-progress", GROUP_WORKAREAS + "workarea01-last-time-completed", + public static final String GROUP_WORKAREA = "workarea#"; - GROUP_WORKAREAS + "workarea02-id", GROUP_WORKAREAS + "workarea02-name", - GROUP_WORKAREAS + "workarea02-cutting-height", GROUP_WORKAREAS + "workarea02-enabled", - GROUP_WORKAREAS + "workarea02-progress", GROUP_WORKAREAS + "workarea02-last-time-completed", + public static final ArrayList CHANNEL_WORKAREAS = new ArrayList<>(List.of(GROUP_WORKAREA + "01-id", + GROUP_WORKAREA + "01-name", GROUP_WORKAREA + "01-cutting-height", GROUP_WORKAREA + "01-enabled", + GROUP_WORKAREA + "01-progress", GROUP_WORKAREA + "01-last-time-completed", - GROUP_WORKAREAS + "workarea03-id", GROUP_WORKAREAS + "workarea03-name", - GROUP_WORKAREAS + "workarea03-cutting-height", GROUP_WORKAREAS + "workarea03-enabled", - GROUP_WORKAREAS + "workarea03-progress", GROUP_WORKAREAS + "workarea03-last-time-completed", + GROUP_WORKAREA + "02-id", GROUP_WORKAREA + "02-name", GROUP_WORKAREA + "02-cutting-height", + GROUP_WORKAREA + "02-enabled", GROUP_WORKAREA + "02-progress", GROUP_WORKAREA + "02-last-time-completed", - GROUP_WORKAREAS + "workarea04-id", GROUP_WORKAREAS + "workarea04-name", - GROUP_WORKAREAS + "workarea04-cutting-height", GROUP_WORKAREAS + "workarea04-enabled", - GROUP_WORKAREAS + "workarea04-progress", GROUP_WORKAREAS + "workarea04-last-time-completed", + GROUP_WORKAREA + "03-id", GROUP_WORKAREA + "03-name", GROUP_WORKAREA + "03-cutting-height", + GROUP_WORKAREA + "03-enabled", GROUP_WORKAREA + "03-progress", GROUP_WORKAREA + "03-last-time-completed", - GROUP_WORKAREAS + "workarea05-id", GROUP_WORKAREAS + "workarea05-name", - GROUP_WORKAREAS + "workarea05-cutting-height", GROUP_WORKAREAS + "workarea05-enabled", - GROUP_WORKAREAS + "workarea05-progress", GROUP_WORKAREAS + "workarea05-last-time-completed", + GROUP_WORKAREA + "04-id", GROUP_WORKAREA + "04-name", GROUP_WORKAREA + "04-cutting-height", + GROUP_WORKAREA + "04-enabled", GROUP_WORKAREA + "04-progress", GROUP_WORKAREA + "04-last-time-completed", - GROUP_WORKAREAS + "workarea06-id", GROUP_WORKAREAS + "workarea06-name", - GROUP_WORKAREAS + "workarea06-cutting-height", GROUP_WORKAREAS + "workarea06-enabled", - GROUP_WORKAREAS + "workarea06-progress", GROUP_WORKAREAS + "workarea06-last-time-completed", + GROUP_WORKAREA + "05-id", GROUP_WORKAREA + "05-name", GROUP_WORKAREA + "05-cutting-height", + GROUP_WORKAREA + "05-enabled", GROUP_WORKAREA + "05-progress", GROUP_WORKAREA + "05-last-time-completed", - GROUP_WORKAREAS + "workarea07-id", GROUP_WORKAREAS + "workarea07-name", - GROUP_WORKAREAS + "workarea07-cutting-height", GROUP_WORKAREAS + "workarea07-enabled", - GROUP_WORKAREAS + "workarea07-progress", GROUP_WORKAREAS + "workarea07-last-time-completed", + GROUP_WORKAREA + "06-id", GROUP_WORKAREA + "06-name", GROUP_WORKAREA + "06-cutting-height", + GROUP_WORKAREA + "06-enabled", GROUP_WORKAREA + "06-progress", GROUP_WORKAREA + "06-last-time-completed", - GROUP_WORKAREAS + "workarea08-id", GROUP_WORKAREAS + "workarea08-name", - GROUP_WORKAREAS + "workarea08-cutting-height", GROUP_WORKAREAS + "workarea08-enabled", - GROUP_WORKAREAS + "workarea08-progress", GROUP_WORKAREAS + "workarea08-last-time-completed", + GROUP_WORKAREA + "07-id", GROUP_WORKAREA + "07-name", GROUP_WORKAREA + "07-cutting-height", + GROUP_WORKAREA + "07-enabled", GROUP_WORKAREA + "07-progress", GROUP_WORKAREA + "07-last-time-completed", - GROUP_WORKAREAS + "workarea09-id", GROUP_WORKAREAS + "workarea09-name", - GROUP_WORKAREAS + "workarea09-cutting-height", GROUP_WORKAREAS + "workarea09-enabled", - GROUP_WORKAREAS + "workarea09-progress", GROUP_WORKAREAS + "workarea09-last-time-completed", + GROUP_WORKAREA + "08-id", GROUP_WORKAREA + "08-name", GROUP_WORKAREA + "08-cutting-height", + GROUP_WORKAREA + "08-enabled", GROUP_WORKAREA + "08-progress", GROUP_WORKAREA + "08-last-time-completed", - GROUP_WORKAREAS + "workarea10-id", GROUP_WORKAREAS + "workarea10-name", - GROUP_WORKAREAS + "workarea10-cutting-height", GROUP_WORKAREAS + "workarea10-enabled", - GROUP_WORKAREAS + "workarea10-progress", GROUP_WORKAREAS + "workarea10-last-time-completed")); + GROUP_WORKAREA + "09-id", GROUP_WORKAREA + "09-name", GROUP_WORKAREA + "09-cutting-height", + GROUP_WORKAREA + "09-enabled", GROUP_WORKAREA + "09-progress", GROUP_WORKAREA + "09-last-time-completed", + + GROUP_WORKAREA + "10-id", GROUP_WORKAREA + "10-name", GROUP_WORKAREA + "10-cutting-height", + GROUP_WORKAREA + "10-enabled", GROUP_WORKAREA + "10-progress", GROUP_WORKAREA + "10-last-time-completed")); // Command Channel ids - public static final String GROUP_COMMANDS = ""; // no channel group in use at the moment, we'll possibly introduce - // this in a future release - public static final String CHANNEL_COMMAND_START = GROUP_COMMANDS + "start"; - public static final String CHANNEL_COMMAND_RESUME_SCHEDULE = GROUP_COMMANDS + "resume_schedule"; - public static final String CHANNEL_COMMAND_PAUSE = GROUP_COMMANDS + "pause"; - public static final String CHANNEL_COMMAND_PARK = GROUP_COMMANDS + "park"; - public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMANDS + "park_until_next_schedule"; - public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMANDS + "park_until_further_notice"; + public static final String GROUP_COMMAND = "command#"; + + public static final String CHANNEL_COMMAND_START = GROUP_COMMAND + "start"; + public static final String CHANNEL_COMMAND_RESUME_SCHEDULE = GROUP_COMMAND + "resume_schedule"; + public static final String CHANNEL_COMMAND_PAUSE = GROUP_COMMAND + "pause"; + public static final String CHANNEL_COMMAND_PARK = GROUP_COMMAND + "park"; + public static final String CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE = GROUP_COMMAND + "park_until_next_schedule"; + public static final String CHANNEL_COMMAND_PARK_UNTIL_NOTICE = GROUP_COMMAND + "park_until_further_notice"; // Automower properties public static final String AUTOMOWER_ID = "mowerId"; @@ -331,7 +293,7 @@ public class AutomowerBindingConstants { put(75, "Connection changed"); put(76, "Connection NOT changed"); put(77, "Com board not available"); - put(78, "Slipped - Mower has Slipped.Situation not solved with moving pattern"); + put(78, "Slipped - Mower has Slipped. Situation not solved with moving pattern"); put(79, "Invalid battery combination - Invalid combination of different battery types."); put(80, "Cutting system imbalance Warning"); put(81, "Safety function faulty"); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java index 2849c9345ce98..24a1f74365b93 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerCommand.java @@ -18,6 +18,7 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.automower.internal.AutomowerBindingConstants; import org.openhab.core.thing.ChannelUID; /** @@ -25,12 +26,13 @@ */ @NonNullByDefault public enum AutomowerCommand { - START("Start", "start"), - RESUME_SCHEDULE("ResumeSchedule", "resume_schedule"), - PAUSE("Pause", "pause"), - PARK("Park", "park"), - PARK_UNTIL_NEXT_SCHEDULE("ParkUntilNextSchedule", "park_until_next_schedule"), - PARK_UNTIL_FURTHER_NOTICE("ParkUntilFurtherNotice", "park_until_further_notice"); + START("Start", AutomowerBindingConstants.CHANNEL_COMMAND_START), + RESUME_SCHEDULE("ResumeSchedule", AutomowerBindingConstants.CHANNEL_COMMAND_RESUME_SCHEDULE), + PAUSE("Pause", AutomowerBindingConstants.CHANNEL_COMMAND_PAUSE), + PARK("Park", AutomowerBindingConstants.CHANNEL_COMMAND_PARK), + PARK_UNTIL_NEXT_SCHEDULE("ParkUntilNextSchedule", + AutomowerBindingConstants.CHANNEL_COMMAND_PARK_UNTIL_NEXT_SCHEDULE), + PARK_UNTIL_FURTHER_NOTICE("ParkUntilFurtherNotice", AutomowerBindingConstants.CHANNEL_COMMAND_PARK_UNTIL_NOTICE); private static final Map CHANNEL_TO_CMD_MAP = new HashMap<>(); diff --git a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java index d1c3650c5730c..4a7451bcba05a 100644 --- a/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java +++ b/bundles/org.openhab.binding.automower/src/main/java/org/openhab/binding/automower/internal/things/AutomowerHandler.java @@ -124,7 +124,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { sendAutomowerCalendarTask(command, channelUID.getId()); } else if (CHANNEL_STAYOUTZONES.contains(channelUID.getId())) { String[] channelIDSplit = channelUID.getId().split("-"); - int index = Integer.parseInt(channelIDSplit[0].substring("zone".length())) - 1; + int index = Integer.parseInt(channelIDSplit[0].substring(GROUP_STAYOUTZONE.length())) - 1; if ("enabled".equals(channelIDSplit[0])) { if (command instanceof OnOffType cmd) { sendAutomowerStayOutZone(index, ((cmd == OnOffType.ON) ? true : false)); @@ -132,7 +132,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } } else if (CHANNEL_WORKAREAS.contains(channelUID.getId())) { String[] channelIDSplit = channelUID.getId().split("-"); - int index = Integer.parseInt(channelIDSplit[0].substring("workarea".length())) - 1; + int index = Integer.parseInt(channelIDSplit[0].substring(GROUP_WORKAREA.length())) - 1; if ("enabled".equals(channelIDSplit[0])) { if (command instanceof OnOffType cmd) { sendAutomowerWorkAreaEnable(index, ((cmd == OnOffType.ON) ? true : false)); @@ -176,6 +176,8 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Sending command '{}'", commandName); getCommandValue(command).ifPresentOrElse(duration -> sendAutomowerCommand(commandName, duration), () -> sendAutomowerCommand(commandName)); + + updateState(channelUID, OnOffType.OFF); }); } } @@ -359,7 +361,6 @@ public void sendAutomowerCalendarTask(Long workAreaId, short[] start, short[] du boolean[] tuesday, boolean[] wednesday, boolean[] thursday, boolean[] friday, boolean[] saturday, boolean[] sunday) { if (isValidResult(mowerState)) { - List calendarTaskArray = new ArrayList<>(); for (int i = 0; (i < start.length) && (i < duration.length) && (i < monday.length) && (i < tuesday.length) @@ -787,21 +788,21 @@ private void updateChannelState(@Nullable Mower mower) { long nextStartTimestamp = mower.getAttributes().getPlanner().getNextStartTimestamp(); // If next start timestamp is 0 it means the mower should start now, so using current timestamp if (nextStartTimestamp == 0L) { - updateState(CHANNEL_PLANNER_NEXT_START, UnDefType.NULL); + updateState(CHANNEL_STATUS_NEXT_START, UnDefType.NULL); } else { - updateState(CHANNEL_PLANNER_NEXT_START, + updateState(CHANNEL_STATUS_NEXT_START, new DateTimeType(toZonedDateTime(nextStartTimestamp, mowerZoneId))); } - updateState(CHANNEL_PLANNER_OVERRIDE_ACTION, + updateState(CHANNEL_STATUS_OVERRIDE_ACTION, new StringType(mower.getAttributes().getPlanner().getOverride().getAction().name())); RestrictedReason restrictedReason = mower.getAttributes().getPlanner().getRestrictedReason(); if (restrictedReason != null) { - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, new StringType(restrictedReason.name())); + updateState(CHANNEL_STATUS_RESTRICTED_REASON, new StringType(restrictedReason.name())); } else { - updateState(CHANNEL_PLANNER_RESTRICTED_REASON, UnDefType.NULL); + updateState(CHANNEL_STATUS_RESTRICTED_REASON, UnDefType.NULL); } - updateState(CHANNEL_PLANNER_EXTERNAL_REASON, + updateState(CHANNEL_STATUS_EXTERNAL_REASON, new DecimalType(mower.getAttributes().getPlanner().getExternalReason())); updateState(CHANNEL_SETTING_CUTTING_HEIGHT, diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties index 50a343a4fff17..84b1bfd088b0f 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/i18n/automower.properties @@ -25,6 +25,17 @@ thing-type.config.automower.bridge.appSecret.description = The Application Secre thing-type.config.automower.bridge.pollingInterval.label = Polling Interval thing-type.config.automower.bridge.pollingInterval.description = How often the available automowers should be queried in seconds +# channel group types + +channel-group-type.automower.calendarTasksType.label = Calendar Tasks +channel-group-type.automower.commandsType.label = Commands +channel-group-type.automower.positionsType.label = Positions +channel-group-type.automower.settingsType.label = Settings +channel-group-type.automower.statisticsType.label = Statistics +channel-group-type.automower.statusType.label = Status +channel-group-type.automower.stayoutZonesType.label = Stayout Zones +channel-group-type.automower.workAreasType.label = Work Areas + # channel types channel-type.automower.activityType.label = Activity @@ -39,28 +50,28 @@ channel-type.automower.activityType.state.option.PARKED_IN_CS = Parked in chargi channel-type.automower.activityType.state.option.STOPPED_IN_GARDEN = Stopped in garden channel-type.automower.batteryType.label = Battery channel-type.automower.batteryType.description = The battery level of the mower at the time of last update -channel-type.automower.calendarTasksDurationType.label = Duration -channel-type.automower.calendarTasksDurationType.description = Duration time -channel-type.automower.calendarTasksDurationType.state.pattern = %1$tHh:%1$tMm -channel-type.automower.calendarTasksFridayType.label = Enabled on Fridays -channel-type.automower.calendarTasksFridayType.description = Enabled on Fridays -channel-type.automower.calendarTasksMondayType.label = Enabled on Mondays -channel-type.automower.calendarTasksMondayType.description = Enabled on Mondays -channel-type.automower.calendarTasksSaturdayType.label = Enabled on Saturdays -channel-type.automower.calendarTasksSaturdayType.description = Enabled on Saturdays -channel-type.automower.calendarTasksStartType.label = Start Time -channel-type.automower.calendarTasksStartType.description = Start time relative to midnight -channel-type.automower.calendarTasksStartType.state.pattern = %1$tHh:%1$tMm -channel-type.automower.calendarTasksSundayType.label = Enabled on Sundays -channel-type.automower.calendarTasksSundayType.description = Enabled on Sundays -channel-type.automower.calendarTasksThursdayType.label = Enabled on Thursdays -channel-type.automower.calendarTasksThursdayType.description = Enabled on Thursdays -channel-type.automower.calendarTasksTuesdayType.label = Enabled on Tuesdays -channel-type.automower.calendarTasksTuesdayType.description = Enabled on Tuesdays -channel-type.automower.calendarTasksWednesdayType.label = Enabled on Wednesdays -channel-type.automower.calendarTasksWednesdayType.description = Enabled on Wednesdays -channel-type.automower.calendarTasksWorkAreaIdType.label = Work Area Id of Calendar -channel-type.automower.calendarTasksWorkAreaIdType.description = Work Area Id mapped to this calendar +channel-type.automower.calendarTaskDurationType.label = Duration +channel-type.automower.calendarTaskDurationType.description = Duration time +channel-type.automower.calendarTaskDurationType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTaskFridayType.label = Enabled on Fridays +channel-type.automower.calendarTaskFridayType.description = Enabled on Fridays +channel-type.automower.calendarTaskMondayType.label = Enabled on Mondays +channel-type.automower.calendarTaskMondayType.description = Enabled on Mondays +channel-type.automower.calendarTaskSaturdayType.label = Enabled on Saturdays +channel-type.automower.calendarTaskSaturdayType.description = Enabled on Saturdays +channel-type.automower.calendarTaskStartType.label = Start Time +channel-type.automower.calendarTaskStartType.description = Start time relative to midnight +channel-type.automower.calendarTaskStartType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTaskSundayType.label = Enabled on Sundays +channel-type.automower.calendarTaskSundayType.description = Enabled on Sundays +channel-type.automower.calendarTaskThursdayType.label = Enabled on Thursdays +channel-type.automower.calendarTaskThursdayType.description = Enabled on Thursdays +channel-type.automower.calendarTaskTuesdayType.label = Enabled on Tuesdays +channel-type.automower.calendarTaskTuesdayType.description = Enabled on Tuesdays +channel-type.automower.calendarTaskWednesdayType.label = Enabled on Wednesdays +channel-type.automower.calendarTaskWednesdayType.description = Enabled on Wednesdays +channel-type.automower.calendarTaskWorkAreaIdType.label = Work Area Id of Calendar +channel-type.automower.calendarTaskWorkAreaIdType.description = Work Area Id mapped to this calendar channel-type.automower.errorCodeType.label = Error Code channel-type.automower.errorCodeType.description = The error code at the time of last update channel-type.automower.errorConfirmableType.label = Error Confirmable @@ -117,7 +128,7 @@ channel-type.automower.positionType.description = A waypoint of the mower's acti channel-type.automower.resumeSchedule.label = Resume Schedule channel-type.automower.resumeSchedule.description = Resume schedule channel-type.automower.settingCuttingHeightType.label = Cutting Height -channel-type.automower.settingCuttingHeightType.description = Prescaled cutting height, Range: 1-9 +channel-type.automower.settingCuttingHeightType.description = Prescaled cutting height, range: 1-9 channel-type.automower.settingHeadlightModeType.label = Headlight Mode channel-type.automower.settingHeadlightModeType.description = Information about headlights channel-type.automower.start.label = Start with Duration @@ -174,8 +185,8 @@ channel-type.automower.workAreaIdType.label = Work Area Id channel-type.automower.workAreaIdType.description = Id of the active work area channel-type.automower.workAreaType.label = Work Area Name channel-type.automower.workAreaType.description = Name of the active work area -channel-type.automower.workareaCuttingHeightType.label = Cutting height in percent -channel-type.automower.workareaCuttingHeightType.description = Cutting height in percent. 0-100 +channel-type.automower.workareaCuttingHeightType.label = Cutting Height +channel-type.automower.workareaCuttingHeightType.description = Cutting height of the work area, range: 0-100 channel-type.automower.workareaEnabledType.label = Work Area Enabled channel-type.automower.workareaEnabledType.description = If the work area is enabled or disabled channel-type.automower.workareaIdType.label = Work Area Id @@ -186,7 +197,7 @@ channel-type.automower.workareaNameType.label = Work Area Name channel-type.automower.workareaNameType.description = Name of the work area channel-type.automower.workareaProgressType.label = Work Area Progress channel-type.automower.workareaProgressType.description = The progress on a work area. EPOS mowers and systematic mowing work areas only. -channel-type.automower.zoneDirtyType.label = Stayout zones Dirty Flag +channel-type.automower.zoneDirtyType.label = Stayout Zones Dirty Flag channel-type.automower.zoneDirtyType.description = If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone. channel-type.automower.zoneEnabledType.label = Stayout Zone Enabled channel-type.automower.zoneEnabledType.description = If the Stayout zone is enabled, the Automower® will not access the zone @@ -195,6 +206,31 @@ channel-type.automower.zoneIdType.description = Id of the Stayout zone channel-type.automower.zoneNameType.label = Stayout Zone Name channel-type.automower.zoneNameType.description = The name of the Stayout zone +# channel types + +channel-type.automower.calendarTasksDurationType.label = Duration +channel-type.automower.calendarTasksDurationType.description = Duration time +channel-type.automower.calendarTasksDurationType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTasksFridayType.label = Enabled on Fridays +channel-type.automower.calendarTasksFridayType.description = Enabled on Fridays +channel-type.automower.calendarTasksMondayType.label = Enabled on Mondays +channel-type.automower.calendarTasksMondayType.description = Enabled on Mondays +channel-type.automower.calendarTasksSaturdayType.label = Enabled on Saturdays +channel-type.automower.calendarTasksSaturdayType.description = Enabled on Saturdays +channel-type.automower.calendarTasksStartType.label = Start Time +channel-type.automower.calendarTasksStartType.description = Start time relative to midnight +channel-type.automower.calendarTasksStartType.state.pattern = %1$tHh:%1$tMm +channel-type.automower.calendarTasksSundayType.label = Enabled on Sundays +channel-type.automower.calendarTasksSundayType.description = Enabled on Sundays +channel-type.automower.calendarTasksThursdayType.label = Enabled on Thursdays +channel-type.automower.calendarTasksThursdayType.description = Enabled on Thursdays +channel-type.automower.calendarTasksTuesdayType.label = Enabled on Tuesdays +channel-type.automower.calendarTasksTuesdayType.description = Enabled on Tuesdays +channel-type.automower.calendarTasksWednesdayType.label = Enabled on Wednesdays +channel-type.automower.calendarTasksWednesdayType.description = Enabled on Wednesdays +channel-type.automower.calendarTasksWorkAreaIdType.label = Work Area Id of Calendar +channel-type.automower.calendarTasksWorkAreaIdType.description = Work Area Id mapped to this calendar + # thing types thing-type.automower.automower.channel.last-position.description = Last Position diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml index 825d3043f0a46..afa3f9262542e 100644 --- a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/thing/thing-types.xml @@ -42,318 +42,16 @@ An automatic lawn mower - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + N/A @@ -385,6 +83,361 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + String @@ -566,7 +619,7 @@ Number - Prescaled cutting height, Range: 1-9 + Prescaled cutting height, range: 1-9 @@ -674,54 +727,54 @@ - + Number:Time Start time relative to midnight - + Number:Time Duration time - + Switch Enabled on Mondays - + Switch Enabled on Tuesdays - + Switch Enabled on Wednesdays - + Switch Enabled on Thursdays - + Switch Enabled on Fridays - + Switch Enabled on Saturdays - + Switch Enabled on Sundays - + Number Work Area Id mapped to this calendar @@ -731,7 +784,7 @@ Switch - + If the stay-out zones are synchronized with the Husqvarna cloud. If the map is dirty you can not enable or disable a stay-out zone. @@ -774,8 +827,8 @@ Number:Dimensionless - - Cutting height in percent. 0-100 + + Cutting height of the work area, range: 0-100 diff --git a/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml new file mode 100644 index 0000000000000..606801abaa6ea --- /dev/null +++ b/bundles/org.openhab.binding.automower/src/main/resources/OH-INF/update/update.xml @@ -0,0 +1,973 @@ + + + + + + + + automower:nameType + + + automower:modeType + + + automower:activityType + + + automower:inactiveReasonType + + + automower:stateType + + + automower:workAreaIdType + + + automower:workAreaType + + + automower:lastUpdateType + + + system:battery-level + + + automower:errorCodeType + + + automower:errorMessageType + + + automower:errorTimestampType + + + automower:errorConfirmableType + + + automower:plannerNextStartTimestampType + + + automower:plannerOverrideActionType + + + automower:plannerRestrictedReasonType + + + automower:plannerExternalReasonType + + + + + automower:settingCuttingHeightType + + + automower:settingHeadlightModeType + + + + + automower:statCuttingBladeUsageTimeType + + + automower:statDownTimeType + + + automower:statNumberOfChargingCyclesType + + + automower:statNumberOfCollisionsType + + + automower:statTotalChargingTimeType + + + automower:statTotalCuttingTimeType + + + automower:statTotalCuttingPercentType + + + automower:statTotalDriveDistanceType + + + automower:statTotalRunningTimeType + + + automower:statTotalSearchingTimeType + + + automower:statTotalSearchingPercentType + + + automower:statUpTimeType + + + + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + automower:calendarTasksStartType + + + automower:calendarTasksDurationType + + + automower:calendarTasksMondayType + + + automower:calendarTasksTuesdayType + + + automower:calendarTasksWednesdayType + + + automower:calendarTasksThursdayType + + + automower:calendarTasksFridayType + + + automower:calendarTasksSaturdayType + + + automower:calendarTasksSundayType + + + automower:calendarTasksWorkAreaIdType + + + automower:workareaNameType + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + automower:lastPositionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + automower:positionType + + + + + automower:zoneDirtyType + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + automower:zoneIdType + + + automower:zoneNameType + + + automower:zoneEnabledType + + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + automower:workareaIdType + + + automower:workareaNameType + + + automower:workareaCuttingHeightType + + + automower:workareaEnabledType + + + automower:workareaProgressType + + + automower:workareaLastTimeCompletedType + + + + + automower:start + + + automower:resumeSchedule + + + automower:pause + + + automower:park + + + automower:parkUntilNextSchedule + + + automower:parkUntilFurtherNotice + + + +