added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.robonect-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-robonect" description="Robonect Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.robonect/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link RobonectBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectBindingConstants {
private static final String BINDING_ID = "robonect";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_AUTOMOWER = new ThingTypeUID(BINDING_ID, "mower");
// List of all Channel ids
public static final String CHANNEL_MOWER_NAME = "name";
public static final String CHANNEL_STATUS_BATTERY = "battery";
public static final String CHANNEL_STATUS_DURATION = "status-duration";
public static final String CHANNEL_STATUS_HOURS = "mowing-hours";
public static final String CHANNEL_STATUS_MODE = "mode";
public static final String CHANNEL_STATUS = "status";
public static final String CHANNEL_MOWER_START = "start";
public static final String CHANNEL_MOWER_STATUS_OFFLINE_TRIGGER = "offlineTrigger";
public static final String CHANNEL_TIMER_STATUS = "timer-status";
public static final String CHANNEL_TIMER_NEXT_TIMER = "timer-next";
public static final String CHANNEL_WLAN_SIGNAL = "wlan-signal";
public static final String CHANNEL_JOB = "job";
public static final String CHANNEL_ERROR_CODE = "error-code";
public static final String CHANNEL_ERROR_MESSAGE = "error-message";
public static final String CHANNEL_ERROR_DATE = "error-date";
public static final String CHANNEL_LAST_ERROR_CODE = "last-error-code";
public static final String CHANNEL_LAST_ERROR_MESSAGE = "last-error-message";
public static final String CHANNEL_LAST_ERROR_DATE = "last-error-date";
public static final String CHANNEL_HEALTH_TEMP = "health-temperature";
public static final String CHANNEL_HEALTH_HUM = "health-humidity";
public static final String PROPERTY_COMPILED = "compiled";
public static final String PROPERTY_COMMENT = "comment";
}

View File

@@ -0,0 +1,352 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.AuthenticationStore;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.util.B64Code;
import org.openhab.binding.robonect.internal.model.ErrorList;
import org.openhab.binding.robonect.internal.model.ModelParser;
import org.openhab.binding.robonect.internal.model.MowerInfo;
import org.openhab.binding.robonect.internal.model.MowerMode;
import org.openhab.binding.robonect.internal.model.Name;
import org.openhab.binding.robonect.internal.model.RobonectAnswer;
import org.openhab.binding.robonect.internal.model.VersionInfo;
import org.openhab.binding.robonect.internal.model.cmd.Command;
import org.openhab.binding.robonect.internal.model.cmd.ErrorCommand;
import org.openhab.binding.robonect.internal.model.cmd.ModeCommand;
import org.openhab.binding.robonect.internal.model.cmd.NameCommand;
import org.openhab.binding.robonect.internal.model.cmd.StartCommand;
import org.openhab.binding.robonect.internal.model.cmd.StatusCommand;
import org.openhab.binding.robonect.internal.model.cmd.StopCommand;
import org.openhab.binding.robonect.internal.model.cmd.VersionCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link RobonectClient} class is responsible to communicate with the robonect module via it's HTTP interface.
*
* The API of the module is documented here: http://robonect.de/viewtopic.php?f=10&t=37
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectClient {
private final Logger logger = LoggerFactory.getLogger(RobonectClient.class);
private final String baseUrl;
private final HttpClient httpClient;
private final ModelParser parser;
private boolean jobRunning;
/**
* The {@link JobSettings} class holds the values required for starting a job.
*/
public static class JobSettings {
private static final String TIME_REGEX = "^[012]\\d:\\d\\d$";
private final Logger logger = LoggerFactory.getLogger(RobonectClient.class);
private ModeCommand.RemoteStart remoteStart;
private ModeCommand.Mode after;
private int duration;
/**
* returns the 'remote start' setting for the job. See {@link ModeCommand.RemoteStart} for details.
*
* @return - the remote start settings for the job.
*/
public ModeCommand.RemoteStart getRemoteStart() {
if (remoteStart != null) {
return remoteStart;
} else {
logger.debug("No explicit remote start set. Return STANDARD.");
return ModeCommand.RemoteStart.STANDARD;
}
}
/**
* Sets the desired 'remote start' settings for the job.
*
* @param remoteStart - The 'remote start' settings. See {@link ModeCommand.RemoteStart} for the allowed modes.
*/
public JobSettings withRemoteStart(ModeCommand.RemoteStart remoteStart) {
this.remoteStart = remoteStart;
return this;
}
/**
* Returns the mode the mower should be set to after the job is complete.
*
* @return - the mode after compleness of the job.
*/
public ModeCommand.Mode getAfterMode() {
return after;
}
/**
* Sets the mode after the mower is complete with the job.
*
* @param after - the desired mode after job completeness.
*/
public JobSettings withAfterMode(ModeCommand.Mode after) {
this.after = after;
return this;
}
public int getDuration() {
return duration;
}
public JobSettings withDuration(int duration) {
this.duration = duration;
return this;
}
}
private static class BasicResult implements Authentication.Result {
private final HttpHeader header;
private final URI uri;
private final String value;
public BasicResult(HttpHeader header, URI uri, String value) {
this.header = header;
this.uri = uri;
this.value = value;
}
public URI getURI() {
return this.uri;
}
public void apply(Request request) {
request.header(this.header, this.value);
}
public String toString() {
return String.format("Basic authentication result for %s", this.uri);
}
}
/**
* Creates an instance of RobonectClient which allows to communicate with the specified endpoint via the passed
* httpClient instance.
*
* @param httpClient - The HttpClient to use for the communication.
* @param endpoint - The endpoint information for connecting and issuing commands.
*/
public RobonectClient(HttpClient httpClient, RobonectEndpoint endpoint) {
this.httpClient = httpClient;
this.baseUrl = "http://" + endpoint.getIpAddress() + "/json";
this.parser = new ModelParser();
if (endpoint.isUseAuthentication()) {
addPreemptiveAuthentication(httpClient, endpoint);
}
}
private void addPreemptiveAuthentication(HttpClient httpClient, RobonectEndpoint endpoint) {
AuthenticationStore auth = httpClient.getAuthenticationStore();
URI uri = URI.create(baseUrl);
auth.addAuthenticationResult(new BasicResult(HttpHeader.AUTHORIZATION, uri, "Basic "
+ B64Code.encode(endpoint.getUser() + ":" + endpoint.getPassword(), StandardCharsets.ISO_8859_1)));
}
/**
* returns general mower information. See {@MowerInfo} for the detailed information.
*
* @return - the general mower information including a general success status.
*/
public MowerInfo getMowerInfo() {
String responseString = sendCommand(new StatusCommand());
MowerInfo mowerInfo = parser.parse(responseString, MowerInfo.class);
if (jobRunning) {
// mode might have been changed on the mower. Also Mode JOB does not really exist on the mower, thus cannot
// be checked here
if (mowerInfo.getStatus().getMode() == MowerMode.AUTO
|| mowerInfo.getStatus().getMode() == MowerMode.HOME) {
jobRunning = false;
} else if (mowerInfo.getError() != null) {
jobRunning = false;
}
}
return mowerInfo;
}
/**
* sends a start command to the mower.
*
* @return - a general answer with success status.
*/
public RobonectAnswer start() {
String responseString = sendCommand(new StartCommand());
return parser.parse(responseString, RobonectAnswer.class);
}
/**
* sends a stop command to the mower.
*
* @return - a general answer with success status.
*/
public RobonectAnswer stop() {
String responseString = sendCommand(new StopCommand());
return parser.parse(responseString, RobonectAnswer.class);
}
/**
* resets the errors on the mower.
*
* @return - a general answer with success status.
*/
public RobonectAnswer resetErrors() {
String responseString = sendCommand(new ErrorCommand().withReset(true));
return parser.parse(responseString, RobonectAnswer.class);
}
/**
* returns the list of all errors happened since last reset.
*
* @return - the list of errors.
*/
public ErrorList errorList() {
String responseString = sendCommand(new ErrorCommand());
return parser.parse(responseString, ErrorList.class);
}
/**
* Sets the mode of the mower. See {@link ModeCommand.Mode} for details about the available modes. Not allowed is
* mode
* {@link ModeCommand.Mode#JOB}.
*
* @param mode - the desired mower mode.
* @return - a general answer with success status.
*/
public RobonectAnswer setMode(ModeCommand.Mode mode) {
String responseString = sendCommand(createCommand(mode));
if (jobRunning) {
jobRunning = false;
}
return parser.parse(responseString, RobonectAnswer.class);
}
private ModeCommand createCommand(ModeCommand.Mode mode) {
return new ModeCommand(mode);
}
/**
* Returns the name of the mower.
*
* @return - The name including a general answer with success status.
*/
public Name getName() {
String responseString = sendCommand(new NameCommand());
return parser.parse(responseString, Name.class);
}
/**
* Allows to set the name of the mower.
*
* @param name - the desired name.
* @return - The resulting name including a general answer with success status.
*/
public Name setName(String name) {
String responseString = sendCommand(new NameCommand().withNewName(name));
return parser.parse(responseString, Name.class);
}
private String sendCommand(Command command) {
try {
if (logger.isDebugEnabled()) {
logger.debug("send HTTP GET to: {} ", command.toCommandURL(baseUrl));
}
ContentResponse response = httpClient.newRequest(command.toCommandURL(baseUrl)).method(HttpMethod.GET)
.timeout(30000, TimeUnit.MILLISECONDS).send();
String responseString = null;
// jetty uses UTF-8 as default encoding. However, HTTP 1.1 specifies ISO_8859_1
if (StringUtils.isBlank(response.getEncoding())) {
responseString = new String(response.getContent(), StandardCharsets.ISO_8859_1);
} else {
// currently v0.9e Robonect does not specifiy the encoding. But if later versions will
// add, it should work with the default method to get the content as string.
responseString = response.getContentAsString();
}
if (logger.isDebugEnabled()) {
logger.debug("Response body was: {} ", responseString);
}
return responseString;
} catch (ExecutionException | TimeoutException | InterruptedException e) {
throw new RobonectCommunicationException("Could not send command " + command.toCommandURL(baseUrl), e);
}
}
/**
* Retrieve the version information of the mower and module. See {@link VersionInfo} for details.
*
* @return - the Version Information including the successful status.
*/
public VersionInfo getVersionInfo() {
String versionResponse = sendCommand(new VersionCommand());
return parser.parse(versionResponse, VersionInfo.class);
}
public boolean isJobRunning() {
return jobRunning;
}
public RobonectAnswer startJob(JobSettings settings) {
Command jobCommand = new ModeCommand(ModeCommand.Mode.JOB).withRemoteStart(settings.remoteStart)
.withAfter(settings.after).withDuration(settings.duration);
String responseString = sendCommand(jobCommand);
RobonectAnswer answer = parser.parse(responseString, RobonectAnswer.class);
if (answer.isSuccessful()) {
jobRunning = true;
} else {
jobRunning = false;
}
return answer;
}
public RobonectAnswer stopJob(JobSettings settings) {
RobonectAnswer answer = null;
if (jobRunning) {
answer = setMode(settings.after);
if (answer.isSuccessful()) {
jobRunning = false;
}
} else {
answer = new RobonectAnswer();
// this is not an error, thus return success
answer.setSuccessful(true);
}
return answer;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal;
/**
* This exception is thrown if there was an error in communication with the mower. As a mower is a moving object, this
* error is kind of expected and the error situation is handled in the handler.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectCommunicationException extends RuntimeException {
public RobonectCommunicationException(String message) {
super(message);
}
public RobonectCommunicationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal;
import org.eclipse.jetty.util.StringUtil;
/**
* The {@link RobonectEndpoint} is holds the information required to a Robonect endpoint.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectEndpoint {
private final String ipAddress;
private final String user;
private final String password;
private boolean useAuthentication = false;
public RobonectEndpoint(String ipAddress, String user, String password) {
this.ipAddress = ipAddress;
this.user = user;
this.password = password;
this.useAuthentication = StringUtil.isNotBlank(user) && StringUtil.isNotBlank(password);
}
public String getIpAddress() {
return ipAddress;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public boolean isUseAuthentication() {
return useAuthentication;
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal;
import static org.openhab.binding.robonect.internal.RobonectBindingConstants.THING_TYPE_AUTOMOWER;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.robonect.internal.handler.RobonectHandler;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link RobonectHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Marco Meyer - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.robonect")
public class RobonectHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_AUTOMOWER);
private HttpClient httpClient;
private TimeZoneProvider timeZoneProvider;
@Activate
public RobonectHandlerFactory(@Reference HttpClientFactory httpClientFactory,
@Reference TimeZoneProvider timeZoneProvider) {
this.httpClient = httpClientFactory.getCommonHttpClient();
this.timeZoneProvider = timeZoneProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_AUTOMOWER)) {
return new RobonectHandler(thing, httpClient, timeZoneProvider);
}
return null;
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.config;
/**
* The {@link JobChannelConfig} class holds the channel configuration for the Job channel.
*
* @author Marco Meyer - Initial contribution
*/
public class JobChannelConfig {
private String remoteStart;
private String afterMode;
private int duration;
public String getRemoteStart() {
return remoteStart;
}
public void setRemoteStart(String remoteStart) {
this.remoteStart = remoteStart;
}
public String getAfterMode() {
return afterMode;
}
public void setAfterMode(String afterMode) {
this.afterMode = afterMode;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.config;
/**
*
* This class acts simply a structure for holding the thing configuration.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectConfig {
private String host;
private String user;
private String password;
private int pollInterval;
private int offlineTimeout;
private String timezone;
public String getHost() {
return host;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public int getPollInterval() {
return pollInterval;
}
public int getOfflineTimeout() {
return offlineTimeout;
}
public String getTimezone() {
return timezone;
}
}

View File

@@ -0,0 +1,446 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.handler;
import static org.openhab.binding.robonect.internal.RobonectBindingConstants.*;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.robonect.internal.RobonectClient;
import org.openhab.binding.robonect.internal.RobonectCommunicationException;
import org.openhab.binding.robonect.internal.RobonectEndpoint;
import org.openhab.binding.robonect.internal.config.JobChannelConfig;
import org.openhab.binding.robonect.internal.config.RobonectConfig;
import org.openhab.binding.robonect.internal.model.ErrorEntry;
import org.openhab.binding.robonect.internal.model.ErrorList;
import org.openhab.binding.robonect.internal.model.MowerInfo;
import org.openhab.binding.robonect.internal.model.Name;
import org.openhab.binding.robonect.internal.model.RobonectAnswer;
import org.openhab.binding.robonect.internal.model.VersionInfo;
import org.openhab.binding.robonect.internal.model.cmd.ModeCommand;
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.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonSyntaxException;
/**
* The {@link RobonectHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* The channels are periodically updated by polling the mower via HTTP in a separate thread.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(RobonectHandler.class);
private ScheduledFuture<?> pollingJob;
private HttpClient httpClient;
private TimeZoneProvider timeZoneProvider;
private ZoneId timeZone;
private RobonectClient robonectClient;
public RobonectHandler(Thing thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) {
super(thing);
this.httpClient = httpClient;
this.timeZoneProvider = timeZoneProvider;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
try {
if (command instanceof RefreshType) {
refreshChannels(channelUID);
} else {
sendCommand(channelUID, command);
}
updateStatus(ThingStatus.ONLINE);
} catch (RobonectCommunicationException rce) {
logger.debug("Failed to communicate with the mower. Taking it offline.", rce);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, rce.getMessage());
}
}
private void sendCommand(ChannelUID channelUID, Command command) {
switch (channelUID.getId()) {
case CHANNEL_MOWER_NAME:
if (command instanceof StringType) {
updateName((StringType) command);
} else {
logger.debug("Got name update of type {} but StringType is expected.",
command.getClass().getName());
}
break;
case CHANNEL_STATUS_MODE:
if (command instanceof StringType) {
setMowerMode(command);
} else {
logger.debug("Got job remote start update of type {} but StringType is expected.",
command.getClass().getName());
}
break;
case CHANNEL_MOWER_START:
if (command instanceof OnOffType) {
handleStartStop((OnOffType) command);
} else {
logger.debug("Got stopped update of type {} but OnOffType is expected.",
command.getClass().getName());
}
break;
case CHANNEL_JOB:
if (command instanceof OnOffType) {
handleJobCommand(channelUID, command);
} else {
logger.debug("Got job update of type {} but OnOffType is expected.", command.getClass().getName());
}
break;
}
}
private void handleJobCommand(ChannelUID channelUID, Command command) {
JobChannelConfig jobConfig = getThing().getChannel(channelUID.getId()).getConfiguration()
.as(JobChannelConfig.class);
if (command == OnOffType.ON) {
robonectClient.startJob(
new RobonectClient.JobSettings().withAfterMode(ModeCommand.Mode.valueOf(jobConfig.getAfterMode()))
.withRemoteStart(ModeCommand.RemoteStart.valueOf(jobConfig.getRemoteStart()))
.withDuration(jobConfig.getDuration()));
} else if (command == OnOffType.OFF) {
robonectClient.stopJob(
new RobonectClient.JobSettings().withAfterMode(ModeCommand.Mode.valueOf(jobConfig.getAfterMode())));
}
}
private void refreshChannels(ChannelUID channelUID) {
switch (channelUID.getId()) {
case CHANNEL_MOWER_NAME:
case CHANNEL_STATUS_BATTERY:
case CHANNEL_STATUS:
case CHANNEL_STATUS_DURATION:
case CHANNEL_STATUS_HOURS:
case CHANNEL_STATUS_MODE:
case CHANNEL_MOWER_START:
case CHANNEL_TIMER_NEXT_TIMER:
case CHANNEL_TIMER_STATUS:
case CHANNEL_WLAN_SIGNAL:
case CHANNEL_JOB:
refreshMowerInfo();
break;
default:
case CHANNEL_LAST_ERROR_CODE:
case CHANNEL_LAST_ERROR_DATE:
case CHANNEL_LAST_ERROR_MESSAGE:
refreshLastErrorInfo();
break;
}
}
private void setMowerMode(Command command) {
String modeStr = command.toFullString();
ModeCommand.Mode newMode = ModeCommand.Mode.valueOf(modeStr.toUpperCase());
if (robonectClient.setMode(newMode).isSuccessful()) {
updateState(CHANNEL_STATUS_MODE, new StringType(newMode.name()));
} else {
refreshMowerInfo();
}
}
private void logErrorFromResponse(RobonectAnswer result) {
if (!result.isSuccessful()) {
logger.debug("Could not send EOD Trigger. Robonect error message: {}", result.getErrorMessage());
}
}
private void handleStartStop(final OnOffType command) {
RobonectAnswer answer = null;
boolean currentlyStopped = robonectClient.getMowerInfo().getStatus().isStopped();
if (command == OnOffType.ON && currentlyStopped) {
answer = robonectClient.start();
} else if (command == OnOffType.OFF && !currentlyStopped) {
answer = robonectClient.stop();
}
if (answer != null) {
if (answer.isSuccessful()) {
updateState(CHANNEL_MOWER_START, command);
} else {
logErrorFromResponse(answer);
refreshMowerInfo();
}
}
}
private void updateName(StringType command) {
String newName = command.toFullString();
Name name = robonectClient.setName(newName);
if (name.isSuccessful()) {
updateState(CHANNEL_MOWER_NAME, new StringType(name.getName()));
} else {
logErrorFromResponse(name);
refreshMowerInfo();
}
}
private void refreshMowerInfo() {
MowerInfo info = robonectClient.getMowerInfo();
if (info.isSuccessful()) {
if (info.getError() != null) {
updateErrorInfo(info.getError());
refreshLastErrorInfo();
} else {
clearErrorInfo();
}
updateState(CHANNEL_MOWER_NAME, new StringType(info.getName()));
updateState(CHANNEL_STATUS_BATTERY, new DecimalType(info.getStatus().getBattery()));
updateState(CHANNEL_STATUS, new DecimalType(info.getStatus().getStatus().getStatusCode()));
updateState(CHANNEL_STATUS_DURATION,
new QuantityType<>(info.getStatus().getDuration(), SmartHomeUnits.SECOND));
updateState(CHANNEL_STATUS_HOURS, new QuantityType<>(info.getStatus().getHours(), SmartHomeUnits.HOUR));
updateState(CHANNEL_STATUS_MODE, new StringType(info.getStatus().getMode().name()));
updateState(CHANNEL_MOWER_START, info.getStatus().isStopped() ? OnOffType.OFF : OnOffType.ON);
if (info.getHealth() != null) {
updateState(CHANNEL_HEALTH_TEMP,
new QuantityType<>(info.getHealth().getTemperature(), SIUnits.CELSIUS));
updateState(CHANNEL_HEALTH_HUM,
new QuantityType(info.getHealth().getHumidity(), SmartHomeUnits.PERCENT));
}
if (info.getTimer() != null) {
if (info.getTimer().getNext() != null) {
updateNextTimer(info);
}
updateState(CHANNEL_TIMER_STATUS, new StringType(info.getTimer().getStatus().name()));
}
updateState(CHANNEL_WLAN_SIGNAL, new DecimalType(info.getWlan().getSignal()));
updateState(CHANNEL_JOB, robonectClient.isJobRunning() ? OnOffType.ON : OnOffType.OFF);
} else {
logger.error("Could not retrieve mower info. Robonect error response message: {}", info.getErrorMessage());
}
}
private void clearErrorInfo() {
updateState(CHANNEL_ERROR_DATE, UnDefType.UNDEF);
updateState(CHANNEL_ERROR_CODE, UnDefType.UNDEF);
updateState(CHANNEL_ERROR_MESSAGE, UnDefType.UNDEF);
}
private void updateErrorInfo(ErrorEntry error) {
if (error.getErrorMessage() != null) {
updateState(CHANNEL_ERROR_MESSAGE, new StringType(error.getErrorMessage()));
}
if (error.getErrorCode() != null) {
updateState(CHANNEL_ERROR_CODE, new DecimalType(error.getErrorCode().intValue()));
}
if (error.getDate() != null) {
State dateTime = convertUnixToDateTimeType(error.getUnix());
updateState(CHANNEL_ERROR_DATE, dateTime);
}
}
private void updateNextTimer(MowerInfo info) {
State dateTime = convertUnixToDateTimeType(info.getTimer().getNext().getUnix());
updateState(CHANNEL_TIMER_NEXT_TIMER, dateTime);
}
private State convertUnixToDateTimeType(String unixTimeSec) {
// the value in unixTimeSec represents the time on the robot in its configured timezone. However, it does not
// provide which zone this is. Thus we have to add the zone information from the Thing configuration in order to
// provide correct results.
Instant rawInstant = Instant.ofEpochMilli(Long.valueOf(unixTimeSec) * 1000);
ZoneId timeZoneOfThing = timeZone;
if (timeZoneOfThing == null) {
timeZoneOfThing = timeZoneProvider.getTimeZone();
}
ZoneOffset offsetToConfiguredZone = timeZoneOfThing.getRules().getOffset(rawInstant);
long adjustedTime = rawInstant.getEpochSecond() - offsetToConfiguredZone.getTotalSeconds();
Instant adjustedInstant = Instant.ofEpochMilli(adjustedTime * 1000);
// we provide the time in the format as configured in the openHAB settings
ZonedDateTime zdt = adjustedInstant.atZone(timeZoneProvider.getTimeZone());
return new DateTimeType(zdt);
}
private void refreshVersionInfo() {
VersionInfo info = robonectClient.getVersionInfo();
if (info.isSuccessful()) {
Map<String, String> properties = editProperties();
properties.put(Thing.PROPERTY_SERIAL_NUMBER, info.getRobonect().getSerial());
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, info.getRobonect().getVersion());
properties.put(PROPERTY_COMPILED, info.getRobonect().getCompiled());
properties.put(PROPERTY_COMMENT, info.getRobonect().getComment());
updateProperties(properties);
} else {
logger.debug("Could not retrieve mower version info. Robonect error response message: {}",
info.getErrorMessage());
}
}
private void refreshLastErrorInfo() {
ErrorList errorList = robonectClient.errorList();
if (errorList.isSuccessful()) {
List<ErrorEntry> errors = errorList.getErrors();
if (errors != null && !errors.isEmpty()) {
ErrorEntry lastErrorEntry = errors.get(0);
updateLastErrorChannels(lastErrorEntry);
}
} else {
logger.debug("Could not retrieve mower error list. Robonect error response message: {}",
errorList.getErrorMessage());
}
}
private void updateLastErrorChannels(ErrorEntry error) {
if (error.getErrorMessage() != null) {
updateState(CHANNEL_LAST_ERROR_MESSAGE, new StringType(error.getErrorMessage()));
}
if (error.getErrorCode() != null) {
updateState(CHANNEL_LAST_ERROR_CODE, new DecimalType(error.getErrorCode().intValue()));
}
if (error.getDate() != null) {
State dateTime = convertUnixToDateTimeType(error.getUnix());
updateState(CHANNEL_LAST_ERROR_DATE, dateTime);
}
}
@Override
public void initialize() {
RobonectConfig robonectConfig = getConfigAs(RobonectConfig.class);
RobonectEndpoint endpoint = new RobonectEndpoint(robonectConfig.getHost(), robonectConfig.getUser(),
robonectConfig.getPassword());
String timeZoneString = robonectConfig.getTimezone();
try {
if (timeZoneString != null) {
timeZone = ZoneId.of(timeZoneString);
} else {
logger.warn("No timezone provided, falling back to the default timezone configured in openHAB: '{}'",
timeZoneProvider.getTimeZone());
}
} catch (DateTimeException e) {
logger.warn("Error setting timezone '{}', falling back to the default timezone configured in openHAB: '{}'",
timeZoneString, timeZoneProvider.getTimeZone(), e);
}
try {
httpClient.start();
robonectClient = new RobonectClient(httpClient, endpoint);
} catch (Exception e) {
logger.error("Exception while trying to start http client", e);
throw new RuntimeException("Exception while trying to start http client", e);
}
Runnable runnable = new MowerChannelPoller(TimeUnit.SECONDS.toMillis(robonectConfig.getOfflineTimeout()));
int pollInterval = robonectConfig.getPollInterval();
pollingJob = scheduler.scheduleWithFixedDelay(runnable, 0, pollInterval, TimeUnit.SECONDS);
}
@Override
public void dispose() {
if (pollingJob != null) {
pollingJob.cancel(true);
pollingJob = null;
}
try {
if (httpClient != null) {
httpClient.stop();
httpClient = null;
}
} catch (Exception e) {
logger.debug("Could not stop http client", e);
}
}
/**
* method to inject the robonect client to be used in test cases to allow mocking.
*
* @param robonectClient
*/
protected void setRobonectClient(RobonectClient robonectClient) {
this.robonectClient = robonectClient;
}
private class MowerChannelPoller implements Runnable {
private long offlineSince;
private long offlineTriggerDelay;
private boolean offlineTimeoutTriggered;
private boolean loadVersionInfo = true;
public MowerChannelPoller(long offlineTriggerDelay) {
offlineSince = -1;
this.offlineTriggerDelay = offlineTriggerDelay;
offlineTimeoutTriggered = false;
}
@Override
public void run() {
try {
if (loadVersionInfo) {
refreshVersionInfo();
loadVersionInfo = false;
}
refreshMowerInfo();
updateStatus(ThingStatus.ONLINE);
offlineSince = -1;
offlineTimeoutTriggered = false;
} catch (RobonectCommunicationException rce) {
if (offlineSince < 0) {
offlineSince = System.currentTimeMillis();
}
if (!offlineTimeoutTriggered && System.currentTimeMillis() - offlineSince > offlineTriggerDelay) {
// trigger offline
updateState(CHANNEL_MOWER_STATUS_OFFLINE_TRIGGER, new StringType("OFFLINE_TIMEOUT"));
offlineTimeoutTriggered = true;
}
logger.debug("Failed to communicate with the mower. Taking it offline.", rce);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, rce.getMessage());
loadVersionInfo = true;
} catch (JsonSyntaxException jse) {
// the module sporadically sends invalid json responses. As this is usually recovered with the
// next poll interval, we just log it to debug here.
logger.debug("Failed to parse response.", jse);
}
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import com.google.gson.annotations.SerializedName;
/**
* POJO for deserialize an error entry from a JSON response using GSON.
*
* @author Marco Meyer - Initial contribution
*/
public class ErrorEntry {
private String date;
@SerializedName("error_code")
private Integer errorCode;
@SerializedName("error_message")
private String errorMessage;
private String time;
private String unix;
/**
* @return - the date the error happend in the format "dd.MM.yy"
*/
public String getDate() {
return date;
}
/**
* @return - the error code. Some codes are documented here: http://www.robonect.de/viewtopic.php?f=11&t=110
*/
public Integer getErrorCode() {
return errorCode;
}
/**
* @return - The localized error message from the mower.
*/
public String getErrorMessage() {
return errorMessage;
}
/**
* @return - The time the error happened in the format "HH:mm:ss"
*/
public String getTime() {
return time;
}
/**
* @return - The unix time when the error happened.
*/
public String getUnix() {
return unix;
}
public void setDate(String date) {
this.date = date;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public void setTime(String time) {
this.time = time;
}
public void setUnix(String unix) {
this.unix = unix;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import java.util.List;
/**
* Simple POJO for deserialize the list of errors from the errors command.
*
* @author Marco Meyer - Initial contribution
*/
public class ErrorList extends RobonectAnswer {
private List<ErrorEntry> errors;
/**
* @return - the list of errors.
*/
public List<ErrorEntry> getErrors() {
return errors;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* Health information from the mower. This information is just included if the robonect module runs the firmware
* 1.0 beta or higher.
*
* @author Marco Meyer - Initial contribution
*/
public class Health {
private int temperature;
private int humidity;
/**
* @return - the temperature in °C measured in the mower.
*/
public int getTemperature() {
return temperature;
}
/**
* @return - the humidity in % measured in the mower.
*/
public int getHumidity() {
return humidity;
}
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* This class is responsible for parsing JSNON formatted answers from the Robonect module using the Gson library.
*
* @author Marco Meyer - Initial contribution
*/
public class ModelParser {
private final Gson gson;
/**
* Creates a parser with containing a preconfigured Gson object capable of parsing the JSON answers from the
* Robonect module.
*/
public ModelParser() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MowerStatus.class, new MowerStatusDeserializer());
gsonBuilder.registerTypeAdapter(MowerMode.class, new MowerModeDeserializer());
gsonBuilder.registerTypeAdapter(Timer.TimerMode.class, new TimerModeDeserializer());
this.gson = gsonBuilder.create();
}
/**
* Parses a jsonString to a Java Object of the specified type.
*
* @param jsonString - the json string to parse
* @param type - the class of the type of the expected object to be returned.
* @param <T> - the type of expected return value.
* @return
*/
public <T> T parse(String jsonString, Class<T> type) {
return gson.fromJson(jsonString, type);
}
}

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* The mower information holds the main information from the majority of the available channels. This class is a POJO
* to deserialize the JSON response from the module.
*
* @author Marco Meyer - Initial contribution
*/
public class MowerInfo extends RobonectAnswer {
private String name;
private Status status;
private Timer timer;
private Wlan wlan;
private Health health;
private ErrorEntry error;
/**
* @return - the name of the mower
*/
public String getName() {
return name;
}
/**
* @return - some status information of the mower. See {@link Status} for details.
*/
public Status getStatus() {
return status;
}
/**
* @return - the current timer status information.
*/
public Timer getTimer() {
return timer;
}
/**
* @return - the WLAN signal status.
*/
public Wlan getWlan() {
return wlan;
}
/**
* @return - if the mower is in error status {@link #getStatus()} the error information is returned, null otherwise.
*/
public ErrorEntry getError() {
return error;
}
/**
* @return - the health status information.
*/
public Health getHealth() {
return health;
}
public void setName(String name) {
this.name = name;
}
public void setStatus(Status status) {
this.status = status;
}
public void setTimer(Timer timer) {
this.timer = timer;
}
public void setWlan(Wlan wlan) {
this.wlan = wlan;
}
public void setHealth(Health health) {
this.health = health;
}
public void setError(ErrorEntry error) {
this.error = error;
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* The mower mode from the status information. Please note
* that EOD and JOB from {@link org.openhab.binding.robonect.internal.model.cmd.ModeCommand.Mode}
* are just artificial and are therfore not reported in the status information.
*
* @author Marco Meyer - Initial contribution
*/
public enum MowerMode {
/**
* The AUTO mode
*/
AUTO(0),
/**
* The MANUAL mode
*/
MANUAL(1),
/**
* The HOME mode
*/
HOME(2),
/**
* The DEMO mode
*/
DEMO(3),
/**
* An unknown mode. Actually this mode should never be set but is there if for some reason an unexpected value
* is returned from the module response.
*/
UNKNOWN(99);
private int code;
MowerMode(int code) {
this.code = code;
}
/**
* Translate the numeric mode from the JSON response into enum values.
*
* @param mode - the numeric value of the mode.
* @return - the enum value of the mode.
*/
public static MowerMode fromMode(int mode) {
for (MowerMode mowerMode : MowerMode.values()) {
if (mowerMode.code == mode) {
return mowerMode;
}
}
return UNKNOWN;
}
/**
* @return - The numeric code of the mode.
*/
public int getCode() {
return code;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* This is a Gson desirializer capable of translating numeric modes into enum values.
*
* @author Marco Meyer - Initial contribution
*/
public class MowerModeDeserializer implements JsonDeserializer<MowerMode> {
@Override
public MowerMode deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
int mode = jsonElement.getAsInt();
return MowerMode.fromMode(mode);
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An enumeration for the possible mower status.
*
* @author Marco Meyer - Initial contribution
*/
public enum MowerStatus {
/**
* Status is being detected.
*/
DETECTING_STATUS(0),
/**
* Mower is in charging station.
*/
PARKING(1),
/**
* Mower is mowing.
*/
MOWING(2),
/**
* Mower searches charging station
*/
SEARCH_CHARGING_STATION(3),
/**
* Mower is charging.
*/
CHARGING(4),
/**
* Mower is searching the remote start point.
*/
SEARCHING(5),
/**
* Mower is in error state.
*/
ERROR_STATUS(7),
/**
* Mower lost WLAN signal.
*/
LOST_SIGNAL(8),
/**
* Mower is OFF.
*/
OFF(16),
/**
* Mower is sleeping
*/
SLEEPING(17),
/**
* Mower waits for door to open
*/
DOORDELAY(18),
/**
* unknown status. If the module return any not listed code here it will result in this state in the binding.
*/
UNKNOWN(99);
private static final Logger LOGGER = LoggerFactory.getLogger(MowerStatus.class);
private int statusCode;
MowerStatus(int statusCode) {
this.statusCode = statusCode;
}
/**
* translates a numeric code into an enum value. If code is not known the value {@link #UNKNOWN} is returned.
*
* @param code - the code to translate
* @return - the correpsonding enum value.
*/
public static MowerStatus fromCode(int code) {
for (MowerStatus status : MowerStatus.values()) {
if (status.statusCode == code) {
return status;
}
}
LOGGER.debug("Got an unknown state with code {}", code);
return UNKNOWN;
}
/**
* returns the numeric code of the status.
*
* @return
*/
public int getStatusCode() {
return statusCode;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* This is a Gson deserializer to deserialize numeric mower status codes into enum values.
*
* @author Marco Meyer - Initial contribution
*/
public class MowerStatusDeserializer implements JsonDeserializer<MowerStatus> {
@Override
public MowerStatus deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
int code = jsonElement.getAsInt();
return MowerStatus.fromCode(code);
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* Response holding the name of the mower used in the name command.
*
* @author Marco Meyer - Initial contribution
*/
public class Name extends RobonectAnswer {
private String name;
/**
* @return - The mower name.
*/
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* This object will be part of the status response and holds the next timer execution information.
*
* @author Marco Meyer - Initial contribution
*/
public class NextTimer {
private String date;
private String time;
private String unix;
/**
* @return - The date (dd.MM.yy) of the next timer execution.
*/
public String getDate() {
return date;
}
/**
* @return - the timestamp (HH:mm:ss) of the next timer execution
*/
public String getTime() {
return time;
}
/**
* @return - the next timer execution in the form of a unix timestamp.
*/
public String getUnix() {
return unix;
}
public void setDate(String date) {
this.date = date;
}
public void setTime(String time) {
this.time = time;
}
public void setUnix(String unix) {
this.unix = unix;
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import com.google.gson.annotations.SerializedName;
/**
* The super class of all answers from the robonect module. All answersd derive from this class. An answer is either
* successful where all the information of the subclass will be filled, or it is not successful, and this class will
* hold the error information.
*
* @author Marco Meyer - Initial contribution
*/
public class RobonectAnswer {
private boolean successful;
@SerializedName("error_code")
private Integer errorCode;
@SerializedName("error_message")
private String errorMessage;
/**
* @return - true if the request was successful, false otherwise.
*/
public boolean isSuccessful() {
return successful;
}
/**
* allows to set the successful status for testing.
*
* @param successful
*/
public void setSuccessful(boolean successful) {
this.successful = successful;
}
/**
* @return - in case of a not successful request, the error code, null otherwise.
*/
public Integer getErrorCode() {
return errorCode;
}
/**
* @return - in case of a not successful request, the error message, null otherwise.
*/
public String getErrorMessage() {
return errorMessage;
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* Object holding information from the status section of the mowers status response.
*
* @author Marco Meyer - Initial contribution
*/
public class Status {
private int battery;
private int duration;
private int hours;
private MowerStatus status;
private MowerMode mode;
private boolean stopped;
/**
* @return - the battery level in percent. (0-100)
*/
public int getBattery() {
return battery;
}
/**
* @return - The duration in seconds the mower is already in the current {@link #status}.
*/
public int getDuration() {
return duration;
}
/**
* @return - The hours the mower was in use so far.
*/
public int getHours() {
return hours;
}
/**
* @return - The status the mower is currently in. see {@link MowerStatus} for details.
*/
public MowerStatus getStatus() {
return status;
}
/**
* @return - true if the mower is currentyl stopped, false otherwise.
*/
public boolean isStopped() {
return stopped;
}
/**
* @return - The mode the mower is currently in. See {@link MowerMode} for details.
*/
public MowerMode getMode() {
return mode;
}
public void setBattery(int battery) {
this.battery = battery;
}
public void setDuration(int duration) {
this.duration = duration;
}
public void setHours(int hours) {
this.hours = hours;
}
public void setStatus(MowerStatus status) {
this.status = status;
}
public void setMode(MowerMode mode) {
this.mode = mode;
}
public void setStopped(boolean stopped) {
this.stopped = stopped;
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* An object holding the timer information of the mower status.
*
* @author Marco Meyer - Initial contribution
*/
public class Timer {
/**
* an enum defining the possible timer status.
*/
public enum TimerMode {
/**
* timer is inactive. No timer is set or the mower is not in AUTO mode.
*/
INACTIVE(0),
/**
* timer is active. The period of the timer is active and the mower is executing it in AUTO mode.
*/
ACTIVE(1),
/**
* timer is standby. A timer is set, the mower is in AUTO mode but the timer period did not start yet.
*/
STANDBY(2);
private int code;
TimerMode(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static TimerMode fromCode(int code) {
for (TimerMode status : TimerMode.values()) {
if (status.code == code) {
return status;
}
}
return INACTIVE;
}
}
private TimerMode status;
private NextTimer next;
/**
* @return - the timer mode. see {@link TimerMode}
*/
public TimerMode getStatus() {
return status;
}
/**
* @return - information about when the next timer execution will be. See {@link NextTimer}
*/
public NextTimer getNext() {
return next;
}
public void setStatus(TimerMode status) {
this.status = status;
}
public void setNext(NextTimer next) {
this.next = next;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* Gson deserializer for deserializing timer mode values to the corresponding enum.
*
* @author Marco Meyer - Initial contribution
*/
public class TimerModeDeserializer implements JsonDeserializer<Timer.TimerMode> {
@Override
public Timer.TimerMode deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
int code = jsonElement.getAsInt();
return Timer.TimerMode.fromCode(code);
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* Answer object for holding version information.
*
* @author Marco Meyer - Initial contribution
*/
public class VersionInfo extends RobonectAnswer {
private static final RobonectVersion NA_VERSION = new RobonectVersion();
/**
* encapsulates the robonect version information.
*/
public static class RobonectVersion {
private static final String NA = "n/a";
private String serial;
private String version;
private String compiled;
private String comment;
public RobonectVersion() {
this(NA, NA, NA, NA);
}
public RobonectVersion(String serial, String version, String compiled, String comment) {
this.serial = serial;
this.version = version;
this.compiled = compiled;
this.comment = comment;
}
/**
* @return - The serial number of the robonect module.
*/
public String getSerial() {
return serial;
}
/**
* @return - The firmware version running on the robonect module.
*/
public String getVersion() {
return version;
}
/**
* @return - The date and time the firmware was compiled.
*/
public String getCompiled() {
return compiled;
}
/**
* @return - The comment added to this version.
*/
public String getComment() {
return comment;
}
public void setSerial(String serial) {
this.serial = serial;
}
public void setVersion(String version) {
this.version = version;
}
public void setCompiled(String compiled) {
this.compiled = compiled;
}
public void setComment(String comment) {
this.comment = comment;
}
}
private RobonectVersion robonect;
/**
* @return - the object encapsulating the version information. See {@link RobonectVersion}
*/
public RobonectVersion getRobonect() {
if (robonect != null) {
return robonect;
} else {
return NA_VERSION;
}
}
public void setRobonect(RobonectVersion robonect) {
this.robonect = robonect;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model;
/**
* Object holding the wlan signal strength.
*
* @author Marco Meyer - Initial contribution
*/
public class Wlan {
private int signal;
/**
* @return - The signal strength in dB.
*/
public int getSignal() {
return signal;
}
public void setSignal(int signal) {
this.signal = signal;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.RobonectClient;
/**
*
* Interface implemented by all commands. The robonect module is called with urls like
* http://xxx.xxx.xxx/json?cmd=[command]. The command implementation is responsible to construct the full command url.
*
*
* @author Marco Meyer - Initial contribution
*/
public interface Command {
/**
* Implementations of this interface have to return baseUrl + command specific extensions, where the baseURL
* already is in the form http://xxx.xxx.xxx/json?
*
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return - the full command string like for example for a name command http://xxx.xxx.xxx/json?cmd=name
*/
String toCommandURL(String baseURL);
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.RobonectClient;
/**
* Implementation of the error command allowing to retrieve the list of errors or resetting the list.
*
* @author Marco Meyer - Initial contribution
*/
public class ErrorCommand implements Command {
private boolean reset = false;
/**
* has to be set to 'true' if the errors should be reset.
*
* @param reset - true if errors should be reset, false if the list of errors should be retrieved.
* @return
*/
public ErrorCommand withReset(boolean reset) {
this.reset = reset;
return this;
}
/**
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return - the command for retrieving or resetting the error list.
*/
@Override
public String toCommandURL(String baseURL) {
if (reset) {
return baseURL + "?cmd=error&reset";
} else {
return baseURL + "?cmd=error";
}
}
}

View File

@@ -0,0 +1,195 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.RobonectClient;
/**
*
* The mode commands sets the mower into the corresponding mode. In addition to the mowers standard modes
* (HOME, MAN, AUTO) the module supports following modes:
*
* EOD (End Of Day): The mower is set into HOME mode until midnight. After midnight the module sets the mower in AUTO
* mode
* JOB: The JOB mode triggers a JOB and supports following additional parameter:
* * remoteStart: where to start the job (STANDARD, REMOTE_1 or REMOTE_2)
* * after: The mode to be set after the JOB is done. Allowed are all except JOB.
* * start: the start time in the form HH:MM (H=Hour,M=Minute)
* * end: the end time in the form HH:MM (H=Hour,M=Minute)
* * duration: mowing time in minutes (in combination with start or end time)
*
* @author Marco Meyer - Initial contribution
*/
public class ModeCommand implements Command {
/**
* The available modes. See class documentation for the meanings.
*/
public enum Mode {
HOME(1, "home"),
EOD(2, "eod"),
MANUAL(3, "man"),
AUTO(4, "auto"),
JOB(5, "job");
int code;
String cmd;
Mode(int code, String cmd) {
this.code = code;
this.cmd = cmd;
}
}
/**
* The available remoteStart values.
*/
public enum RemoteStart {
/**
* Start immediatly at the docking station.
*/
STANDARD(0),
/**
* Start at the configured remote 1 location.
*/
REMOTE_1(1),
/**
* Start at the conifugred remote 2 location.
*/
REMOTE_2(2);
int code;
RemoteStart(int code) {
this.code = code;
}
}
private Mode mode;
private RemoteStart remoteStart;
private Mode after;
private String start;
private String end;
private Integer duration;
public ModeCommand(Mode mode) {
this.mode = mode;
}
/**
* sets the desired remoteStart option.
*
* @param remoteStart - the remoteStart option.
* @return - the command instance.
*/
public ModeCommand withRemoteStart(RemoteStart remoteStart) {
this.remoteStart = remoteStart;
return this;
}
/**
* set the mode after the job is done.
*
* @param afterMode - the desired mode after job execution.
* @return - the command instance.
*/
public ModeCommand withAfter(Mode afterMode) {
this.after = afterMode;
return this;
}
/**
* The desired start time in the format HH:MM (H=Hour, M=Minute)
*
* @param startTime - the start time.
* @return - the command instance.
*/
public ModeCommand withStart(String startTime) {
this.start = startTime;
return this;
}
/**
* The desired end time in the format HH:MM (H=Hour, M=Minute)
*
* @param endTime - the end time.
* @return - the command instance.
*/
public ModeCommand withEnd(String endTime) {
this.end = endTime;
return this;
}
/**
* Sets the duration in minutes.
*
* @param durationInMinutes - the duration in minutes.
* @return - the command instance.
*/
public ModeCommand withDuration(Integer durationInMinutes) {
this.duration = durationInMinutes;
return this;
}
/**
* {@inheritDoc}
*
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return
*/
@Override
public String toCommandURL(String baseURL) {
StringBuilder sb = new StringBuilder(baseURL);
sb.append("?cmd=mode&mode=");
sb.append(mode.cmd);
switch (mode) {
case EOD:
case MANUAL:
case AUTO:
case HOME:
break;
case JOB:
if (remoteStart != null) {
sb.append("&remotestart=");
sb.append(remoteStart.code);
}
if (after != null) {
sb.append("&after=");
sb.append(after.code);
}
if (start != null) {
sb.append("&start=");
sb.append(start);
}
if (end != null) {
sb.append("&end=");
sb.append(end);
}
if (duration != null) {
sb.append("&duration=");
sb.append(duration);
}
break;
}
return sb.toString();
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import org.openhab.binding.robonect.internal.RobonectClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The command allows to set or retrieve the mower name.
*
* @author Marco Meyer - Initial contribution
*/
public class NameCommand implements Command {
private final Logger logger = LoggerFactory.getLogger(NameCommand.class);
private String newName;
/**
* sets the mower name.
*
* @param newName - the mower name.
* @return - the command instance.
*/
public NameCommand withNewName(String newName) {
this.newName = newName != null ? newName : "";
return this;
}
/**
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return
*/
@Override
public String toCommandURL(String baseURL) {
if (newName == null) {
return baseURL + "?cmd=name";
} else {
try {
return baseURL + "?cmd=name&name="
+ URLEncoder.encode(newName, StandardCharsets.ISO_8859_1.displayName());
} catch (UnsupportedEncodingException e) {
logger.error("Could not encode name {} ", newName, e);
return baseURL + "?cmd=name";
}
}
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.RobonectClient;
/**
* The command starts the mower if it was stopped.
*
* @author Marco Meyer - Initial contribution
*/
public class StartCommand implements Command {
/**
* {@inheritDoc}
*
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return
*/
@Override
public String toCommandURL(String baseURL) {
return baseURL + "?cmd=start";
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.model.MowerStatus;
/**
* Queries the mowers status. The status holds a lot of status information.
* See {@link MowerStatus}
* or the documentation at: http://www.robonect.de/viewtopic.php?f=11&t=38
*
* @author Marco Meyer - Initial contribution
*/
public class StatusCommand implements Command {
@Override
public String toCommandURL(String baseURL) {
return baseURL + "?cmd=status";
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.RobonectClient;
/**
* Stops the mower if it was started.
*
* @author Marco Meyer - Initial contribution
*/
public class StopCommand implements Command {
/**
* {@inheritDoc}
*
* @param baseURL - will be passed by the {@link RobonectClient} in the form
* http://xxx.xxx.xxx/json?
* @return
*/
@Override
public String toCommandURL(String baseURL) {
return baseURL + "?cmd=stop";
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 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.robonect.internal.model.cmd;
import org.openhab.binding.robonect.internal.model.VersionInfo;
/**
* Queries version information about the mower and the module. See {@link VersionInfo}
* for more information.
*
* @author Marco Meyer - Initial contribution
*/
public class VersionCommand implements Command {
@Override
public String toCommandURL(String baseURL) {
return baseURL + "?cmd=version";
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="robonect" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Robonect Binding</name>
<description>This is the binding for Robonect.</description>
<author>Marco Meyer</author>
</binding:binding>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="channel-type:job:config">
<parameter name="remoteStart" type="text" pattern="^STANDARD|REMOTE_1|REMOTE_2$">
<label>Remote Start</label>
<description>The location to start the mowing job from.</description>
</parameter>
<parameter name="duration" type="integer" min="0" max="60" unit="m">
<context>time</context>
<label>Job Duration</label>
<description>The duration of the job.</description>
<default>0</default>
</parameter>
<parameter name="afterMode" type="text" pattern="^AUTO|HOME|EOD$">
<label>After Job Mode</label>
<description>The Mode to put the mower into after the job is done.</description>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="robonect"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="mower">
<label>Mower</label>
<description>Mower robot connected via robonect module</description>
<channels>
<channel id="name" typeId="nameType"/>
<channel id="battery" typeId="system.battery-level"/>
<channel id="wlan-signal" typeId="system.signal-strength"/>
<channel id="mowing-hours" typeId="hoursType"/>
<channel id="mode" typeId="modeType"/>
<channel id="start" typeId="startType"/>
<channel id="status" typeId="mowerStatusType"/>
<channel id="status-duration" typeId="durationType"/>
<channel id="timer-status" typeId="timerStatusType"/>
<channel id="timer-next" typeId="nextTimerType"/>
<channel id="job" typeId="jobType"/>
<channel id="offlineTrigger" typeId="offlineTriggerType"/>
<channel id="error-code" typeId="errorCodeType"/>
<channel id="error-message" typeId="errorMessageType"/>
<channel id="error-date" typeId="errorDateType"/>
<channel id="last-error-code" typeId="errorCodeType">
<label>Last Error Code</label>
<description>The Error code of the last error occurred</description>
</channel>
<channel id="last-error-message" typeId="errorMessageType">
<label>Last Error Message</label>
<description>The error message of the last error occurred</description>
</channel>
<channel id="last-error-date" typeId="errorDateType">
<label>Last Error Date</label>
<description>The date and time of the last error occurred</description>
</channel>
<channel id="health-temperature" typeId="temperatureType"/>
<channel id="health-humidity" typeId="humidityType"/>
</channels>
<properties>
<property name="vendor">robonect.de</property>
<property name="firmwareVersion">N/A</property>
<property name="compiled">N/A</property>
<property name="serialNumber">N/A</property>
</properties>
<config-description>
<parameter name="host" type="text" required="true">
<context>network-address</context>
<label>Host</label>
<description>Host name or network address of the Robonect module</description>
</parameter>
<parameter name="user" type="text" required="false">
<label>User</label>
<description>The user id if authentication has been enabled on the mower</description>
</parameter>
<parameter name="password" type="text" required="false">
<context>password</context>
<label>Password</label>
<description>The password if authentication has been enabled on the mower</description>
</parameter>
<parameter name="pollInterval" type="integer" required="false" unit="s">
<label>Polling Interval</label>
<default>30</default>
<description>The interval for the binding to poll the mowers status information.
</description>
</parameter>
<parameter name="offlineTimeout" type="integer" required="false" unit="m">
<label>Offline Timeout</label>
<default>30</default>
<description>The maximum time the mower may be offline before the offline trigger is triggered.
</description>
</parameter>
<parameter name="timezone" type="text" required="false">
<label>Timezone</label>
<default>Europe/Berlin</default>
<description>The timezone configured on the robot (e.g. Europe/Berlin).</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="serialType">
<item-type>String</item-type>
<label>Robonect Serial Number</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="versionType">
<item-type>String</item-type>
<label>Robonect Version</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="compiledType">
<item-type>String</item-type>
<label>Robonect Version</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="commentType">
<item-type>String</item-type>
<label>Robonect Firmware Comment</label>
<state readOnly="true"/>
</channel-type>
<channel-type id="nameType">
<item-type>String</item-type>
<label>Mower Name</label>
<description>The name of the mower</description>
<state readOnly="false"/>
</channel-type>
<channel-type id="durationType">
<item-type>Number:Time</item-type>
<label>Status Duration</label>
<description>The number of seconds the mower is in the current status.</description>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<channel-type id="hoursType">
<item-type>Number:Time</item-type>
<label>Total Mowing Hours</label>
<description>The number of total mowing hours</description>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<channel-type id="temperatureType">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>The temperature inside the mower</description>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="humidityType">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>The relative humidity in the mower in percent</description>
<state readOnly="true" pattern="%d %%"/>
</channel-type>
<channel-type id="modeType">
<item-type>String</item-type>
<label>Mower Mode</label>
<description>The mower mode</description>
<state readOnly="false">
<options>
<option value="AUTO">Auto</option>
<option value="HOME">Home</option>
<option value="MANUAL">Manual</option>
<option value="EOD">End of Day</option>
</options>
</state>
</channel-type>
<channel-type id="jobType">
<item-type>Switch</item-type>
<label>Mowing Job</label>
<description>Starts a mowing job</description>
<config-description-ref uri="channel-type:job:config"/>
</channel-type>
<channel-type id="timerStatusType">
<item-type>String</item-type>
<label>Timer Status</label>
<description>The status of the timer</description>
<state readOnly="true">
<options>
<option value="INACTIVE">Inactive</option>
<option value="ACTIVE">Active</option>
<option value="STANDBY">Standby</option>
</options>
</state>
</channel-type>
<channel-type id="mowerStatusType">
<item-type>Number</item-type>
<label>Mower Status</label>
<description>The status of the mower</description>
<state readOnly="true">
<options>
<option value="0">Detecting Status</option>
<option value="1">Parking</option>
<option value="2">Mowing</option>
<option value="3">Search Charging Station</option>
<option value="4">Charging</option>
<option value="5">Searching</option>
<option value="6">Unknown Status 6</option>
<option value="7">Error Status</option>
<option value="16">Off</option>
<option value="17">Sleeping</option>
<option value="99">Unknown</option>
</options>
</state>
</channel-type>
<channel-type id="startType">
<item-type>Switch</item-type>
<label>Start</label>
<description>On if started, Off if stopped</description>
<state readOnly="false"/>
</channel-type>
<channel-type id="offlineTriggerType">
<kind>trigger</kind>
<label>Offline Trigger</label>
<event>
<options>
<option value="OFFLINE_TIMEOUT">Offline Timeout</option>
</options>
</event>
</channel-type>
<channel-type id="nextTimerType">
<item-type>DateTime</item-type>
<label>Next Timer Date</label>
<description>The next date the timer starts</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="errorCodeType">
<item-type>Number</item-type>
<label>Error Code</label>
<description>Error code defined by the mower manufacturer</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="errorMessageType">
<item-type>String</item-type>
<label>Error Message</label>
<description>The error message</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="errorDateType">
<item-type>DateTime</item-type>
<label>Error Date</label>
<description>The date and time the error occurred</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>