added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -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>
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user