From 5e4ca2568c8981274f73fd96c597b6ee100a5411 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Sat, 2 Jul 2022 12:31:51 +0200 Subject: [PATCH] [boschindego] Add new channels (#13040) Fixes #12938 Fixes #13017 Signed-off-by: Jacob Laursen --- .../org.openhab.binding.boschindego/README.md | 44 ++++--- .../internal/BoschIndegoBindingConstants.java | 6 + .../internal/IndegoController.java | 117 ++++++++++++++++++ .../config/BoschIndegoConfiguration.java | 2 +- .../boschindego/internal/dto/Battery.java | 36 ++++++ .../boschindego/internal/dto/Garden.java | 50 ++++++++ .../dto/response/OperatingDataResponse.java | 30 +++++ .../IndegoUnreachableException.java | 35 ++++++ .../internal/handler/BoschIndegoHandler.java | 82 ++++++++---- .../OH-INF/i18n/boschindego.properties | 13 +- .../resources/OH-INF/thing/thing-types.xml | 41 +++++- 11 files changed, 410 insertions(+), 46 deletions(-) create mode 100644 bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Battery.java create mode 100644 bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Garden.java create mode 100644 bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/response/OperatingDataResponse.java create mode 100644 bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/exceptions/IndegoUnreachableException.java diff --git a/bundles/org.openhab.binding.boschindego/README.md b/bundles/org.openhab.binding.boschindego/README.md index 5dee2a598..c4578670b 100644 --- a/bundles/org.openhab.binding.boschindego/README.md +++ b/bundles/org.openhab.binding.boschindego/README.md @@ -8,25 +8,31 @@ His [Java Library](https://github.com/zazaz-de/iot-device-bosch-indego-controlle Currently the binding supports ***indego*** mowers as a thing type with these configuration parameters: -| Parameter | Description | Default | -|--------------------|-----------------------------------------------------------------|---------| -| username | Username for the Bosch Indego account | | -| password | Password for the Bosch Indego account | | -| refresh | The number of seconds between refreshing device state | 180 | -| cuttingTimeRefresh | The number of minutes between refreshing last/next cutting time | 60 | +| Parameter | Description | Default | +|-----------------------|-------------------------------------------------------------------------|---------| +| username | Username for the Bosch Indego account | | +| password | Password for the Bosch Indego account | | +| refresh | The number of seconds between refreshing device state | 180 | +| cuttingTimeMapRefresh | The number of minutes between refreshing last/next cutting time and map | 60 | ## Channels -| Channel | Item Type | Description | -|--------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------| -| state | Number | You can send commands to this channel to control the mower and read the simplified state from it (1=mow, 2=return to dock, 3=pause) | -| errorcode | Number | Error code of the mower (0=no error, readonly) | -| statecode | Number | Detailed state of the mower (readonly) | -| textualstate | String | State as a text. (readonly) | -| ready | Number | Shows if the mower is ready to mow (1=ready, 0=not ready, readonly) | -| mowed | Dimmer | Cut grass in percent (readonly) | -| lastCutting | DateTime | Last cutting time (readonly) | -| nextCutting | DateTime | Next scheduled cutting time (readonly) | +| Channel | Item Type | Description | Writeable | +|--------------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------|-----------| +| state | Number | You can send commands to this channel to control the mower and read the simplified state from it (1=mow, 2=return to dock, 3=pause) | Yes | +| errorcode | Number | Error code of the mower (0=no error) | | +| statecode | Number | Detailed state of the mower | | +| textualstate | String | State as a text. | | +| ready | Number | Shows if the mower is ready to mow (1=ready, 0=not ready) | | +| mowed | Dimmer | Cut grass in percent | | +| lastCutting | DateTime | Last cutting time | | +| nextCutting | DateTime | Next scheduled cutting time | | +| batteryVoltage | Number:ElectricPotential | Battery voltage reported by the device | | +| batteryLevel | Number | Battery level as a percentage (0-100%) | | +| lowBattery | Switch | Low battery warning with possible values on (low battery) and off (battery ok) | | +| batteryTemperature | Number:Temperature | Battery temperature reported by the device | | +| gardenSize | Number:Area | Garden size mapped by the device | | +| gardenMap | Image | Garden map mapped by the device | | ### State Codes @@ -81,6 +87,12 @@ Number Indego_Ready { channel="boschindego:indego:lawnmower:ready" } Dimmer Indego_Mowed { channel="boschindego:indego:lawnmower:mowed" } DateTime Indego_LastCutting { channel="boschindego:indego:lawnmower:lastCutting" } DateTime Indego_NextCutting { channel="boschindego:indego:lawnmower:nextCutting" } +Number:ElectricPotential Indego_BatteryVoltage { channel="boschindego:indego:lawnmower:batteryVoltage" } +Number Indego_BatteryLevel { channel="boschindego:indego:lawnmower:batteryLevel" } +Switch Indego_LowBattery { channel="boschindego:indego:lawnmower:lowBattery" } +Number:Temperature Indego_BatteryTemperature { channel="boschindego:indego:lawnmower:batteryTemperature" } +Number:Area Indego_GardenSize { channel="boschindego:indego:lawnmower:gardenSize" } +Image Indego_GardenMap { channel="boschindego:indego:lawnmower:gardenMap" } ``` ### `indego.sitemap` File diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java index e1c6ff3d5..d3902e77d 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/BoschIndegoBindingConstants.java @@ -40,6 +40,12 @@ public class BoschIndegoBindingConstants { public static final String READY = "ready"; public static final String LAST_CUTTING = "lastCutting"; public static final String NEXT_CUTTING = "nextCutting"; + public static final String BATTERY_VOLTAGE = "batteryVoltage"; + public static final String BATTERY_LEVEL = "batteryLevel"; + public static final String LOW_BATTERY = "lowBattery"; + public static final String BATTERY_TEMPERATURE = "batteryTemperature"; + public static final String GARDEN_SIZE = "gardenSize"; + public static final String GARDEN_MAP = "gardenMap"; public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INDEGO); } diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoController.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoController.java index dd50c8e7c..70c66c9f7 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoController.java +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/IndegoController.java @@ -38,12 +38,15 @@ import org.openhab.binding.boschindego.internal.dto.response.AuthenticationRespo import org.openhab.binding.boschindego.internal.dto.response.DeviceCalendarResponse; import org.openhab.binding.boschindego.internal.dto.response.DeviceStateResponse; import org.openhab.binding.boschindego.internal.dto.response.LocationWeatherResponse; +import org.openhab.binding.boschindego.internal.dto.response.OperatingDataResponse; import org.openhab.binding.boschindego.internal.dto.response.PredictiveLastCuttingResponse; import org.openhab.binding.boschindego.internal.dto.response.PredictiveNextCuttingResponse; import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException; import org.openhab.binding.boschindego.internal.exceptions.IndegoException; import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidCommandException; import org.openhab.binding.boschindego.internal.exceptions.IndegoInvalidResponseException; +import org.openhab.binding.boschindego.internal.exceptions.IndegoUnreachableException; +import org.openhab.core.library.types.RawType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -216,6 +219,9 @@ public class IndegoController { // This will currently not happen because "WWW-Authenticate" header is missing; see below. throw new IndegoAuthenticationException("Context rejected"); } + if (status == HttpStatus.GATEWAY_TIMEOUT_504) { + throw new IndegoUnreachableException("Gateway timeout"); + } if (!HttpStatus.isSuccess(status)) { throw new IndegoException("The request failed with error: " + status); } @@ -256,6 +262,93 @@ public class IndegoController { } } + /** + * Wraps {@link #getRawRequest(String)} into an authenticated session. + * + * @param path the relative path to which the request should be sent + * @return the raw data from the response + * @throws IndegoAuthenticationException if request was rejected as unauthorized + * @throws IndegoException if any communication or parsing error occurred + */ + private RawType getRawRequestWithAuthentication(String path) throws IndegoAuthenticationException, IndegoException { + if (!session.isValid()) { + authenticate(); + } + try { + logger.debug("Session {} valid, skipping authentication", session); + return getRawRequest(path); + } catch (IndegoAuthenticationException e) { + if (logger.isTraceEnabled()) { + logger.trace("Context rejected", e); + } else { + logger.debug("Context rejected: {}", e.getMessage()); + } + session.invalidate(); + authenticate(); + return getRawRequest(path); + } + } + + /** + * Sends a GET request to the server and returns the raw response. + * + * @param path the relative path to which the request should be sent + * @return the raw data from the response + * @throws IndegoAuthenticationException if request was rejected as unauthorized + * @throws IndegoException if any communication or parsing error occurred + */ + private RawType getRawRequest(String path) throws IndegoAuthenticationException, IndegoException { + try { + Request request = httpClient.newRequest(BASE_URL + path).method(HttpMethod.GET).header(CONTEXT_HEADER_NAME, + session.getContextId()); + if (logger.isTraceEnabled()) { + logger.trace("GET request for {}", BASE_URL + path); + } + ContentResponse response = sendRequest(request); + int status = response.getStatus(); + if (status == HttpStatus.UNAUTHORIZED_401) { + // This will currently not happen because "WWW-Authenticate" header is missing; see below. + throw new IndegoAuthenticationException("Context rejected"); + } + if (!HttpStatus.isSuccess(status)) { + throw new IndegoException("The request failed with error: " + status); + } + byte[] data = response.getContent(); + if (data == null) { + throw new IndegoInvalidResponseException("No data returned"); + } + String contentType = response.getMediaType(); + if (contentType == null || contentType.isEmpty()) { + throw new IndegoInvalidResponseException("No content-type returned"); + } + logger.debug("Media download response: type {}, length {}", contentType, data.length); + + return new RawType(data, contentType); + } catch (JsonParseException e) { + throw new IndegoInvalidResponseException("Error parsing response", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IndegoException(e); + } catch (TimeoutException e) { + throw new IndegoException(e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause != null && cause instanceof HttpResponseException) { + Response response = ((HttpResponseException) cause).getResponse(); + if (response.getStatus() == HttpStatus.UNAUTHORIZED_401) { + /* + * When contextId is not valid, the service will respond with HTTP code 401 without + * any "WWW-Authenticate" header, violating RFC 7235. Jetty will then throw + * HttpResponseException. We need to handle this in order to attempt + * reauthentication. + */ + throw new IndegoAuthenticationException("Context rejected", e); + } + } + throw new IndegoException(e); + } + } + /** * Wraps {@link #putRequest(String, Object)} into an authenticated session. * @@ -381,6 +474,30 @@ public class IndegoController { DeviceStateResponse.class); } + /** + * Queries the device operating data from the server. + * Server will request this directly from the device, so operation might be slow. + * + * @return the device state + * @throws IndegoAuthenticationException if request was rejected as unauthorized + * @throws IndegoException if any communication or parsing error occurred + */ + public OperatingDataResponse getOperatingData() throws IndegoAuthenticationException, IndegoException { + return getRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/operatingData", + OperatingDataResponse.class); + } + + /** + * Queries the map generated by the device from the server. + * + * @return the garden map + * @throws IndegoAuthenticationException if request was rejected as unauthorized + * @throws IndegoException if any communication or parsing error occurred + */ + public RawType getMap() throws IndegoAuthenticationException, IndegoException { + return getRawRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/map"); + } + /** * Queries the calendar. * diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/config/BoschIndegoConfiguration.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/config/BoschIndegoConfiguration.java index 03148db98..ef34f68b3 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/config/BoschIndegoConfiguration.java +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/config/BoschIndegoConfiguration.java @@ -25,5 +25,5 @@ public class BoschIndegoConfiguration { public @Nullable String username; public @Nullable String password; public long refresh = 180; - public long cuttingTimeRefresh = 60; + public long cuttingTimeMapRefresh = 60; } diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Battery.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Battery.java new file mode 100644 index 000000000..8aef3cef1 --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Battery.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2010-2022 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.boschindego.internal.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * Battery data. + * + * @author Jacob Laursen - Initial contribution + */ +public class Battery { + public double voltage; + + public int cycles; + + public double discharge; + + @SerializedName("ambient_temp") + public int ambientTemperature; + + @SerializedName("battery_temp") + public int batteryTemperature; + + public int percent; +} diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Garden.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Garden.java new file mode 100644 index 000000000..1a6f198ca --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/Garden.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2010-2022 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.boschindego.internal.dto; + +import com.google.gson.annotations.SerializedName; + +/** + * Garden data. + * + * @author Jacob Laursen - Initial contribution + */ +public class Garden { + public long id; + + public String name; + + @SerializedName("signal_id") + public byte signalId; + + public int size; + + @SerializedName("inner_bounds") + public int innerBounds; + + public int cuts; + + public int runtime; + + public int charge; + + public int bumps; + + public int stops; + + @SerializedName("last_mow") + public int lastMow; + + @SerializedName("map_cell_size") + public int mapCellSize; +} diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/response/OperatingDataResponse.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/response/OperatingDataResponse.java new file mode 100644 index 000000000..385f3a8f7 --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/dto/response/OperatingDataResponse.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2010-2022 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.boschindego.internal.dto.response; + +import org.openhab.binding.boschindego.internal.dto.Battery; +import org.openhab.binding.boschindego.internal.dto.Garden; +import org.openhab.binding.boschindego.internal.dto.response.runtime.DeviceStateRuntimes; + +/** + * Response for operating data. + * + * @author Jacob Laursen - Initial contribution + */ +public class OperatingDataResponse { + public DeviceStateRuntimes runtime; + + public Battery battery; + + public Garden garden; +} diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/exceptions/IndegoUnreachableException.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/exceptions/IndegoUnreachableException.java new file mode 100644 index 000000000..1af704f87 --- /dev/null +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/exceptions/IndegoUnreachableException.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 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.boschindego.internal.exceptions; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * {@link IndegoUnreachableException} is thrown on gateway timeout, which + * means that Bosch services cannot connect to the device. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +public class IndegoUnreachableException extends IndegoException { + + private static final long serialVersionUID = -7952585411438042139L; + + public IndegoUnreachableException(String message) { + super(message); + } + + public IndegoUnreachableException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java index da247be33..0dec921f0 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java +++ b/bundles/org.openhab.binding.boschindego/src/main/java/org/openhab/binding/boschindego/internal/handler/BoschIndegoHandler.java @@ -28,13 +28,18 @@ import org.openhab.binding.boschindego.internal.IndegoController; import org.openhab.binding.boschindego.internal.config.BoschIndegoConfiguration; import org.openhab.binding.boschindego.internal.dto.DeviceCommand; import org.openhab.binding.boschindego.internal.dto.response.DeviceStateResponse; +import org.openhab.binding.boschindego.internal.dto.response.OperatingDataResponse; import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException; import org.openhab.binding.boschindego.internal.exceptions.IndegoException; 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.PercentType; +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.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -63,7 +68,7 @@ public class BoschIndegoHandler extends BaseThingHandler { private @NonNullByDefault({}) IndegoController controller; private @Nullable ScheduledFuture statePollFuture; - private @Nullable ScheduledFuture cuttingTimePollFuture; + private @Nullable ScheduledFuture cuttingTimeMapPollFuture; private boolean propertiesInitialized; private int previousStateCode; @@ -96,10 +101,11 @@ public class BoschIndegoHandler extends BaseThingHandler { controller = new IndegoController(httpClient, username, password); updateStatus(ThingStatus.UNKNOWN); - this.statePollFuture = scheduler.scheduleWithFixedDelay(this::refreshStateWithExceptionHandling, 0, - config.refresh, TimeUnit.SECONDS); - this.cuttingTimePollFuture = scheduler.scheduleWithFixedDelay(this::refreshCuttingTimesWithExceptionHandling, 0, - config.cuttingTimeRefresh, TimeUnit.MINUTES); + this.statePollFuture = scheduler.scheduleWithFixedDelay(this::refreshStateAndOperatingDataWithExceptionHandling, + 0, config.refresh, TimeUnit.SECONDS); + this.cuttingTimeMapPollFuture = scheduler.scheduleWithFixedDelay( + this::refreshCuttingTimesAndMapWithExceptionHandling, 0, config.cuttingTimeMapRefresh, + TimeUnit.MINUTES); } @Override @@ -110,21 +116,21 @@ public class BoschIndegoHandler extends BaseThingHandler { pollFuture.cancel(true); } this.statePollFuture = null; - pollFuture = this.cuttingTimePollFuture; + pollFuture = this.cuttingTimeMapPollFuture; if (pollFuture != null) { pollFuture.cancel(true); } - this.cuttingTimePollFuture = null; + this.cuttingTimeMapPollFuture = null; } @Override public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand {} for channel {}", command, channelUID); try { if (command == RefreshType.REFRESH) { handleRefreshCommand(channelUID.getId()); return; } - if (command instanceof DecimalType && channelUID.getId().equals(STATE)) { sendCommand(((DecimalType) command).intValue()); } @@ -144,11 +150,21 @@ public class BoschIndegoHandler extends BaseThingHandler { case ERRORCODE: case STATECODE: case READY: - this.refreshState(); + refreshState(); break; case LAST_CUTTING: case NEXT_CUTTING: - this.refreshCuttingTimes(); + refreshCuttingTimes(); + break; + case BATTERY_LEVEL: + case LOW_BATTERY: + case BATTERY_VOLTAGE: + case BATTERY_TEMPERATURE: + case GARDEN_SIZE: + refreshOperatingData(); + break; + case GARDEN_MAP: + refreshMap(); break; } } @@ -178,14 +194,13 @@ public class BoschIndegoHandler extends BaseThingHandler { logger.debug("Sending command {}", command); updateState(TEXTUAL_STATE, UnDefType.UNDEF); controller.sendCommand(command); - state = controller.getState(); - updateStatus(ThingStatus.ONLINE); - updateState(state); + refreshState(); } - private void refreshStateWithExceptionHandling() { + private void refreshStateAndOperatingDataWithExceptionHandling() { try { refreshState(); + refreshOperatingData(); } catch (IndegoAuthenticationException e) { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/offline.comm-error.authentication-failure"); @@ -201,7 +216,6 @@ public class BoschIndegoHandler extends BaseThingHandler { } DeviceStateResponse state = controller.getState(); - updateStatus(ThingStatus.ONLINE); updateState(state); // When state code changed, refresh cutting times immediately. @@ -211,15 +225,9 @@ public class BoschIndegoHandler extends BaseThingHandler { } } - private void refreshCuttingTimesWithExceptionHandling() { - try { - refreshCuttingTimes(); - } catch (IndegoAuthenticationException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, - "@text/offline.comm-error.authentication-failure"); - } catch (IndegoException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); - } + private void refreshOperatingData() throws IndegoAuthenticationException, IndegoException { + updateOperatingData(controller.getOperatingData()); + updateStatus(ThingStatus.ONLINE); } private void refreshCuttingTimes() throws IndegoAuthenticationException, IndegoException { @@ -244,6 +252,24 @@ public class BoschIndegoHandler extends BaseThingHandler { } } + private void refreshCuttingTimesAndMapWithExceptionHandling() { + try { + refreshCuttingTimes(); + refreshMap(); + } catch (IndegoAuthenticationException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/offline.comm-error.authentication-failure"); + } catch (IndegoException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage()); + } + } + + private void refreshMap() throws IndegoAuthenticationException, IndegoException { + if (isLinked(GARDEN_MAP)) { + updateState(GARDEN_MAP, controller.getMap()); + } + } + private void updateState(DeviceStateResponse state) { DeviceStatus deviceStatus = DeviceStatus.fromCode(state.state); int status = getStatusFromCommand(deviceStatus.getAssociatedCommand()); @@ -260,6 +286,14 @@ public class BoschIndegoHandler extends BaseThingHandler { updateState(TEXTUAL_STATE, new StringType(deviceStatus.getMessage(translationProvider))); } + private void updateOperatingData(OperatingDataResponse operatingData) { + updateState(BATTERY_VOLTAGE, new QuantityType<>(operatingData.battery.voltage, Units.VOLT)); + updateState(BATTERY_LEVEL, new DecimalType(operatingData.battery.percent)); + updateState(LOW_BATTERY, OnOffType.from(operatingData.battery.percent < 20)); + updateState(BATTERY_TEMPERATURE, new QuantityType<>(operatingData.battery.batteryTemperature, SIUnits.CELSIUS)); + updateState(GARDEN_SIZE, new QuantityType<>(operatingData.garden.size, SIUnits.SQUARE_METRE)); + } + private boolean isReadyToMow(DeviceStatus deviceStatus, int error) { return deviceStatus.isReadyToMow() && error == 0; } diff --git a/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/i18n/boschindego.properties b/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/i18n/boschindego.properties index 158fba460..0cdf3ef84 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/i18n/boschindego.properties +++ b/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/i18n/boschindego.properties @@ -10,8 +10,8 @@ thing-type.boschindego.indego.description = Indego which supports the connect fe # thing types config -thing-type.config.boschindego.indego.cuttingTimeRefresh.label = Cutting Time Refresh Interval -thing-type.config.boschindego.indego.cuttingTimeRefresh.description = The number of minutes between refreshing last/next cutting time. +thing-type.config.boschindego.indego.cuttingTimeMapRefresh.label = Cutting Time/Map Refresh Interval +thing-type.config.boschindego.indego.cuttingTimeMapRefresh.description = The number of minutes between refreshing last/next cutting time and map. thing-type.config.boschindego.indego.password.label = Password thing-type.config.boschindego.indego.password.description = Password for the Bosch Indego account. thing-type.config.boschindego.indego.refresh.label = Refresh Interval @@ -21,8 +21,16 @@ thing-type.config.boschindego.indego.username.description = Username for the Bos # channel types +channel-type.boschindego.batteryTemperature.label = Battery Temperature +channel-type.boschindego.batteryTemperature.description = Battery temperature reported by the device +channel-type.boschindego.batteryVoltage.label = Battery Voltage +channel-type.boschindego.batteryVoltage.description = Battery voltage reported by the device channel-type.boschindego.errorcode.label = Error Code channel-type.boschindego.errorcode.description = 0 = no error +channel-type.boschindego.gardenMap.label = Garden Map +channel-type.boschindego.gardenMap.description = Garden map mapped by the device +channel-type.boschindego.gardenSize.label = Garden Size +channel-type.boschindego.gardenSize.description = Garden size mapped by the device channel-type.boschindego.lastCutting.label = Last Cutting channel-type.boschindego.lastCutting.description = Last cutting time channel-type.boschindego.mowed.label = Cut Grass @@ -44,6 +52,7 @@ channel-type.boschindego.textualstate.label = Textual State # thing status descriptions offline.comm-error.authentication-failure = The login credentials are wrong or another client is connected to your Indego account +offline.comm-error.unreachable = Device is unreachable offline.conf-error.missing-password = Password missing offline.conf-error.missing-username = Username missing diff --git a/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/thing/thing-types.xml index 03d63cca4..7e108f0b4 100644 --- a/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.boschindego/src/main/resources/OH-INF/thing/thing-types.xml @@ -16,6 +16,12 @@ + + + + + + @@ -32,9 +38,9 @@ The number of seconds between refreshing device state. 180 - - - The number of minutes between refreshing last/next cutting time. + + + The number of minutes between refreshing last/next cutting time and map. true 60 @@ -132,5 +138,34 @@ Time + + Number:ElectricPotential + + Battery voltage reported by the device + + + + Number:Temperature + + Battery temperature reported by the device + Temperature + + Measurement + Temperature + + + + + Number:Area + + Garden size mapped by the device + + + + Image + + Garden map mapped by the device + +