mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[boschindego] Add channels for last/next cutting time (#12989)
* Add channels for last/next cutting time * Let handleCommand() work synchronously Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
parent
ad9b4fbf79
commit
b886650bae
@ -8,11 +8,12 @@ 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 |
|
||||
|-----------|----------------------------------------------------------------------|
|
||||
| username | Username for the Bosch Indego account |
|
||||
| password | Password for the Bosch Indego account |
|
||||
| refresh | Specifies the refresh interval in seconds (default 180, minimum: 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 |
|
||||
| cuttingTimeRefresh | The number of minutes between refreshing last/next cutting time | 60 |
|
||||
|
||||
## Channels
|
||||
|
||||
@ -24,6 +25,8 @@ Currently the binding supports ***indego*** mowers as a thing type with these
|
||||
| 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) |
|
||||
|
||||
### State Codes
|
||||
|
||||
@ -76,6 +79,8 @@ Number Indego_StateCode { channel="boschindego:indego:lawnmower:statecode" }
|
||||
String Indego_TextualState { channel="boschindego:indego:lawnmower:textualstate" }
|
||||
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" }
|
||||
```
|
||||
|
||||
### `indego.sitemap` File
|
||||
|
@ -38,6 +38,8 @@ public class BoschIndegoBindingConstants {
|
||||
public static final String ERRORCODE = "errorcode";
|
||||
public static final String STATECODE = "statecode";
|
||||
public static final String READY = "ready";
|
||||
public static final String LAST_CUTTING = "lastCutting";
|
||||
public static final String NEXT_CUTTING = "nextCutting";
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INDEGO);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.boschindego.internal.handler.BoschIndegoHandler;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Thing;
|
||||
@ -43,14 +44,16 @@ public class BoschIndegoHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final BoschIndegoTranslationProvider translationProvider;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public BoschIndegoHandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider,
|
||||
ComponentContext componentContext) {
|
||||
final @Reference TimeZoneProvider timeZoneProvider, ComponentContext componentContext) {
|
||||
super.activate(componentContext);
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.translationProvider = new BoschIndegoTranslationProvider(i18nProvider, localeProvider);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,7 +66,7 @@ public class BoschIndegoHandlerFactory extends BaseThingHandlerFactory {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_INDEGO.equals(thingTypeUID)) {
|
||||
return new BoschIndegoHandler(thing, httpClient, translationProvider);
|
||||
return new BoschIndegoHandler(thing, httpClient, translationProvider, timeZoneProvider);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -38,7 +38,8 @@ 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.PredictiveCuttingTimeResponse;
|
||||
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;
|
||||
@ -455,9 +456,8 @@ public class IndegoController {
|
||||
* @throws IndegoException if any communication or parsing error occurred
|
||||
*/
|
||||
public boolean getPredictiveMoving() throws IndegoAuthenticationException, IndegoException {
|
||||
final PredictiveStatus status = getRequestWithAuthentication(
|
||||
SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive", PredictiveStatus.class);
|
||||
return status.enabled;
|
||||
return getRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive",
|
||||
PredictiveStatus.class).enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -473,6 +473,18 @@ public class IndegoController {
|
||||
putRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive", status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries predictive last cutting as {@link Instant}.
|
||||
*
|
||||
* @return predictive last cutting
|
||||
* @throws IndegoAuthenticationException if request was rejected as unauthorized
|
||||
* @throws IndegoException if any communication or parsing error occurred
|
||||
*/
|
||||
public @Nullable Instant getPredictiveLastCutting() throws IndegoAuthenticationException, IndegoException {
|
||||
return getRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive/lastcutting",
|
||||
PredictiveLastCuttingResponse.class).getLastCutting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries predictive next cutting as {@link Instant}.
|
||||
*
|
||||
@ -480,11 +492,9 @@ public class IndegoController {
|
||||
* @throws IndegoAuthenticationException if request was rejected as unauthorized
|
||||
* @throws IndegoException if any communication or parsing error occurred
|
||||
*/
|
||||
public Instant getPredictiveNextCutting() throws IndegoAuthenticationException, IndegoException {
|
||||
final PredictiveCuttingTimeResponse nextCutting = getRequestWithAuthentication(
|
||||
SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive/nextcutting",
|
||||
PredictiveCuttingTimeResponse.class);
|
||||
return nextCutting.getNextCutting();
|
||||
public @Nullable Instant getPredictiveNextCutting() throws IndegoAuthenticationException, IndegoException {
|
||||
return getRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive/nextcutting",
|
||||
PredictiveNextCuttingResponse.class).getNextCutting();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -495,9 +505,8 @@ public class IndegoController {
|
||||
* @throws IndegoException if any communication or parsing error occurred
|
||||
*/
|
||||
public DeviceCalendarResponse getPredictiveExclusionTime() throws IndegoAuthenticationException, IndegoException {
|
||||
final DeviceCalendarResponse calendar = getRequestWithAuthentication(
|
||||
SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive/calendar", DeviceCalendarResponse.class);
|
||||
return calendar;
|
||||
return getRequestWithAuthentication(SERIAL_NUMBER_SUBPATH + this.getSerialNumber() + "/predictive/calendar",
|
||||
DeviceCalendarResponse.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,4 +25,5 @@ public class BoschIndegoConfiguration {
|
||||
public @Nullable String username;
|
||||
public @Nullable String password;
|
||||
public long refresh = 180;
|
||||
public long cuttingTimeRefresh = 60;
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Response for last cutting time.
|
||||
*
|
||||
* @author Jacob Laursen - Initial contribution
|
||||
*/
|
||||
public class PredictiveLastCuttingResponse {
|
||||
@SerializedName("last_mowed")
|
||||
public String lastCutting;
|
||||
|
||||
public @Nullable Instant getLastCutting() {
|
||||
try {
|
||||
return ZonedDateTime.parse(lastCutting).toInstant();
|
||||
} catch (final DateTimeParseException e) {
|
||||
// Ignored
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
@ -23,11 +25,11 @@ import com.google.gson.annotations.SerializedName;
|
||||
*
|
||||
* @author Jacob Laursen - Initial contribution
|
||||
*/
|
||||
public class PredictiveCuttingTimeResponse {
|
||||
public class PredictiveNextCuttingResponse {
|
||||
@SerializedName("mow_next")
|
||||
public String nextCutting;
|
||||
|
||||
public Instant getNextCutting() {
|
||||
public @Nullable Instant getNextCutting() {
|
||||
try {
|
||||
return ZonedDateTime.parse(nextCutting).toInstant();
|
||||
} catch (final DateTimeParseException e) {
|
@ -14,6 +14,8 @@ package org.openhab.binding.boschindego.internal.handler;
|
||||
|
||||
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -28,6 +30,8 @@ import org.openhab.binding.boschindego.internal.dto.DeviceCommand;
|
||||
import org.openhab.binding.boschindego.internal.dto.response.DeviceStateResponse;
|
||||
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.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
@ -55,16 +59,20 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(BoschIndegoHandler.class);
|
||||
private final HttpClient httpClient;
|
||||
private final BoschIndegoTranslationProvider translationProvider;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
private @NonNullByDefault({}) IndegoController controller;
|
||||
private @Nullable ScheduledFuture<?> pollFuture;
|
||||
private long refreshRate;
|
||||
private @Nullable ScheduledFuture<?> statePollFuture;
|
||||
private @Nullable ScheduledFuture<?> cuttingTimePollFuture;
|
||||
private boolean propertiesInitialized;
|
||||
private int previousStateCode;
|
||||
|
||||
public BoschIndegoHandler(Thing thing, HttpClient httpClient, BoschIndegoTranslationProvider translationProvider) {
|
||||
public BoschIndegoHandler(Thing thing, HttpClient httpClient, BoschIndegoTranslationProvider translationProvider,
|
||||
TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.httpClient = httpClient;
|
||||
this.translationProvider = translationProvider;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,29 +94,37 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
controller = new IndegoController(httpClient, username, password);
|
||||
refreshRate = config.refresh;
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
this.pollFuture = scheduler.scheduleWithFixedDelay(this::refreshState, 0, refreshRate, TimeUnit.SECONDS);
|
||||
this.statePollFuture = scheduler.scheduleWithFixedDelay(this::refreshStateWithExceptionHandling, 0,
|
||||
config.refresh, TimeUnit.SECONDS);
|
||||
this.cuttingTimePollFuture = scheduler.scheduleWithFixedDelay(this::refreshCuttingTimesWithExceptionHandling, 0,
|
||||
config.cuttingTimeRefresh, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing Indego handler");
|
||||
ScheduledFuture<?> pollFuture = this.pollFuture;
|
||||
ScheduledFuture<?> pollFuture = this.statePollFuture;
|
||||
if (pollFuture != null) {
|
||||
pollFuture.cancel(true);
|
||||
}
|
||||
this.pollFuture = null;
|
||||
this.statePollFuture = null;
|
||||
pollFuture = this.cuttingTimePollFuture;
|
||||
if (pollFuture != null) {
|
||||
pollFuture.cancel(true);
|
||||
}
|
||||
this.cuttingTimePollFuture = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command == RefreshType.REFRESH) {
|
||||
scheduler.submit(() -> this.refreshState());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (command == RefreshType.REFRESH) {
|
||||
handleRefreshCommand(channelUID.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof DecimalType && channelUID.getId().equals(STATE)) {
|
||||
sendCommand(((DecimalType) command).intValue());
|
||||
}
|
||||
@ -120,6 +136,23 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRefreshCommand(String channelId) throws IndegoAuthenticationException, IndegoException {
|
||||
switch (channelId) {
|
||||
case STATE:
|
||||
case TEXTUAL_STATE:
|
||||
case MOWED:
|
||||
case ERRORCODE:
|
||||
case STATECODE:
|
||||
case READY:
|
||||
this.refreshState();
|
||||
break;
|
||||
case LAST_CUTTING:
|
||||
case NEXT_CUTTING:
|
||||
this.refreshCuttingTimes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCommand(int commandInt) throws IndegoException {
|
||||
DeviceCommand command;
|
||||
switch (commandInt) {
|
||||
@ -150,16 +183,9 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
updateState(state);
|
||||
}
|
||||
|
||||
private void refreshState() {
|
||||
private void refreshStateWithExceptionHandling() {
|
||||
try {
|
||||
if (!propertiesInitialized) {
|
||||
getThing().setProperty(Thing.PROPERTY_SERIAL_NUMBER, controller.getSerialNumber());
|
||||
propertiesInitialized = true;
|
||||
}
|
||||
|
||||
DeviceStateResponse state = controller.getState();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateState(state);
|
||||
refreshState();
|
||||
} catch (IndegoAuthenticationException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/offline.comm-error.authentication-failure");
|
||||
@ -168,6 +194,56 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshState() throws IndegoAuthenticationException, IndegoException {
|
||||
if (!propertiesInitialized) {
|
||||
getThing().setProperty(Thing.PROPERTY_SERIAL_NUMBER, controller.getSerialNumber());
|
||||
propertiesInitialized = true;
|
||||
}
|
||||
|
||||
DeviceStateResponse state = controller.getState();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateState(state);
|
||||
|
||||
// When state code changed, refresh cutting times immediately.
|
||||
if (state.state != previousStateCode) {
|
||||
refreshCuttingTimes();
|
||||
previousStateCode = state.state;
|
||||
}
|
||||
}
|
||||
|
||||
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 refreshCuttingTimes() throws IndegoAuthenticationException, IndegoException {
|
||||
if (isLinked(LAST_CUTTING)) {
|
||||
Instant lastCutting = controller.getPredictiveLastCutting();
|
||||
if (lastCutting != null) {
|
||||
updateState(LAST_CUTTING,
|
||||
new DateTimeType(ZonedDateTime.ofInstant(lastCutting, timeZoneProvider.getTimeZone())));
|
||||
} else {
|
||||
updateState(LAST_CUTTING, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
|
||||
if (isLinked(NEXT_CUTTING)) {
|
||||
Instant nextCutting = controller.getPredictiveNextCutting();
|
||||
if (nextCutting != null) {
|
||||
updateState(NEXT_CUTTING,
|
||||
new DateTimeType(ZonedDateTime.ofInstant(nextCutting, timeZoneProvider.getTimeZone())));
|
||||
} else {
|
||||
updateState(NEXT_CUTTING, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateState(DeviceStateResponse state) {
|
||||
DeviceStatus deviceStatus = DeviceStatus.fromCode(state.state);
|
||||
int status = getStatusFromCommand(deviceStatus.getAssociatedCommand());
|
||||
@ -200,7 +276,7 @@ public class BoschIndegoHandler extends BaseThingHandler {
|
||||
logger.debug("Command is equal to state");
|
||||
return false;
|
||||
}
|
||||
// Cant pause while the mower is docked
|
||||
// Can't pause while the mower is docked
|
||||
if (command == DeviceCommand.PAUSE && deviceStatus.getAssociatedCommand() == DeviceCommand.RETURN) {
|
||||
logger.debug("Can't pause the mower while it's docked or docking");
|
||||
return false;
|
||||
|
@ -10,10 +10,12 @@ 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.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
|
||||
thing-type.config.boschindego.indego.refresh.description = Specifies the refresh interval in seconds.
|
||||
thing-type.config.boschindego.indego.refresh.description = The number of seconds between refreshing device state.
|
||||
thing-type.config.boschindego.indego.username.label = Username
|
||||
thing-type.config.boschindego.indego.username.description = Username for the Bosch Indego account.
|
||||
|
||||
@ -21,7 +23,12 @@ thing-type.config.boschindego.indego.username.description = Username for the Bos
|
||||
|
||||
channel-type.boschindego.errorcode.label = Error Code
|
||||
channel-type.boschindego.errorcode.description = 0 = no error
|
||||
channel-type.boschindego.lastCutting.label = Last Cutting
|
||||
channel-type.boschindego.lastCutting.description = Last cutting time
|
||||
channel-type.boschindego.mowed.label = Cut Grass
|
||||
channel-type.boschindego.mowed.description = Cut grass in percent
|
||||
channel-type.boschindego.nextCutting.label = Next Cutting
|
||||
channel-type.boschindego.nextCutting.description = Next scheduled cutting time
|
||||
channel-type.boschindego.ready.label = Ready
|
||||
channel-type.boschindego.ready.description = Indicates if mower is ready to mow
|
||||
channel-type.boschindego.ready.state.option.0 = not ready
|
||||
|
@ -14,6 +14,8 @@
|
||||
<channel id="statecode" typeId="statecode"/>
|
||||
<channel id="mowed" typeId="mowed"/>
|
||||
<channel id="ready" typeId="ready"/>
|
||||
<channel id="lastCutting" typeId="lastCutting"/>
|
||||
<channel id="nextCutting" typeId="nextCutting"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="username" type="text" required="true">
|
||||
@ -27,9 +29,15 @@
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="60">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in seconds.</description>
|
||||
<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>
|
||||
<advanced>true</advanced>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
@ -96,6 +104,7 @@
|
||||
<channel-type id="mowed">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Cut Grass</label>
|
||||
<description>Cut grass in percent</description>
|
||||
<state readOnly="true" pattern="%d %%"></state>
|
||||
</channel-type>
|
||||
<channel-type id="ready">
|
||||
@ -109,5 +118,19 @@
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="lastCutting">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Cutting</label>
|
||||
<description>Last cutting time</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="nextCutting">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Next Cutting</label>
|
||||
<description>Next scheduled cutting time</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
Loading…
Reference in New Issue
Block a user