[boschindego] Add new channels (#13040)
Fixes #12938 Fixes #13017 Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
53d72dcecb
commit
5e4ca2568c
|
@ -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
|
||||
|
|
|
@ -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<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INDEGO);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
<channel id="ready" typeId="ready"/>
|
||||
<channel id="lastCutting" typeId="lastCutting"/>
|
||||
<channel id="nextCutting" typeId="nextCutting"/>
|
||||
<channel id="batteryVoltage" typeId="batteryVoltage"/>
|
||||
<channel id="batteryLevel" typeId="system.battery-level"/>
|
||||
<channel id="lowBattery" typeId="system.low-battery"/>
|
||||
<channel id="batteryTemperature" typeId="batteryTemperature"/>
|
||||
<channel id="gardenSize" typeId="gardenSize"/>
|
||||
<channel id="gardenMap" typeId="gardenMap"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="username" type="text" required="true">
|
||||
|
@ -32,9 +38,9 @@
|
|||
<description>The number of seconds between refreshing device state.</description>
|
||||
<default>180</default>
|
||||
</parameter>
|
||||
<parameter name="cuttingTimeRefresh" type="integer" min="1">
|
||||
<label>Cutting Time Refresh Interval</label>
|
||||
<description>The number of minutes between refreshing last/next cutting time.</description>
|
||||
<parameter name="cuttingTimeMapRefresh" type="integer" min="1">
|
||||
<label>Cutting Time/Map Refresh Interval</label>
|
||||
<description>The number of minutes between refreshing last/next cutting time and map.</description>
|
||||
<advanced>true</advanced>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
|
@ -132,5 +138,34 @@
|
|||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="batteryVoltage" advanced="true">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Battery Voltage</label>
|
||||
<description>Battery voltage reported by the device</description>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="batteryTemperature" advanced="true">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Battery Temperature</label>
|
||||
<description>Battery temperature reported by the device</description>
|
||||
<category>Temperature</category>
|
||||
<tags>
|
||||
<tag>Measurement</tag>
|
||||
<tag>Temperature</tag>
|
||||
</tags>
|
||||
<state pattern="%d %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="gardenSize">
|
||||
<item-type>Number:Area</item-type>
|
||||
<label>Garden Size</label>
|
||||
<description>Garden size mapped by the device</description>
|
||||
<state pattern="%d %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="gardenMap">
|
||||
<item-type>Image</item-type>
|
||||
<label>Garden Map</label>
|
||||
<description>Garden map mapped by the device</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
|
Loading…
Reference in New Issue