added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.boschindego-${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-boschindego" description="Bosch Indego Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature dependency="true">openhab.tp-jackson</feature>
|
||||
<bundle dependency="true">mvn:org.apache.httpcomponents/httpcore-osgi/4.4.9</bundle>
|
||||
<bundle dependency="true">mvn:org.apache.httpcomponents/httpclient-osgi/4.5.5</bundle>
|
||||
<bundle dependency="true">mvn:commons-codec/commons-codec/1.10</bundle>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.boschindego/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.boschindego.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link BoschIndegoBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Jonas Fleck - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BoschIndegoBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "boschindego";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_INDEGO = new ThingTypeUID(BINDING_ID, "indego");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String STATE = "state";
|
||||
public static final String TEXTUAL_STATE = "textualstate";
|
||||
public static final String MOWED = "mowed";
|
||||
public static final String ERRORCODE = "errorcode";
|
||||
public static final String STATECODE = "statecode";
|
||||
public static final String READY = "ready";
|
||||
}
|
||||
@@ -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.boschindego.internal;
|
||||
|
||||
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.THING_TYPE_INDEGO;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.boschindego.internal.handler.BoschIndegoHandler;
|
||||
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.Component;
|
||||
|
||||
/**
|
||||
* The {@link BoschIndegoHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Jonas Fleck - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.boschindego")
|
||||
public class BoschIndegoHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_INDEGO);
|
||||
|
||||
@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_INDEGO)) {
|
||||
return new BoschIndegoHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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.boschindego.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jonas Fleck - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class IndegoStateConstants {
|
||||
|
||||
public static final int STATE_DOCKED_1 = 258;
|
||||
public static final int STATE_DOCKED_2 = 260;
|
||||
public static final int STATE_DOCKED_3 = 261;
|
||||
public static final int STATE_PAUSED = 517;
|
||||
public static final int STATE_IDLE_IN_LAWN = 519;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* 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.boschindego.internal.handler;
|
||||
|
||||
import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;
|
||||
import static org.openhab.binding.boschindego.internal.IndegoStateConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
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.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.zazaz.iot.bosch.indego.DeviceCommand;
|
||||
import de.zazaz.iot.bosch.indego.DeviceStateInformation;
|
||||
import de.zazaz.iot.bosch.indego.DeviceStatus;
|
||||
import de.zazaz.iot.bosch.indego.IndegoAuthenticationException;
|
||||
import de.zazaz.iot.bosch.indego.IndegoController;
|
||||
import de.zazaz.iot.bosch.indego.IndegoException;
|
||||
|
||||
/**
|
||||
* The {@link BoschIndegoHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Jonas Fleck - Initial contribution
|
||||
*/
|
||||
public class BoschIndegoHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BoschIndegoHandler.class);
|
||||
private final Queue<DeviceCommand> commandQueue = new LinkedList<>();
|
||||
|
||||
private ScheduledFuture<?> pollFuture;
|
||||
|
||||
// If false the request is already scheduled.
|
||||
private boolean shouldReschedule;
|
||||
|
||||
public BoschIndegoHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
// Currently manual refreshing is not possible in the moment
|
||||
return;
|
||||
} else if (channelUID.getId().equals(STATE) && command instanceof DecimalType) {
|
||||
if (command instanceof DecimalType) {
|
||||
sendCommand(((DecimalType) command).intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCommand(int commandInt) {
|
||||
DeviceCommand command;
|
||||
switch (commandInt) {
|
||||
case 1:
|
||||
command = DeviceCommand.MOW;
|
||||
break;
|
||||
case 2:
|
||||
command = DeviceCommand.RETURN;
|
||||
break;
|
||||
case 3:
|
||||
command = DeviceCommand.PAUSE;
|
||||
break;
|
||||
default:
|
||||
logger.error("Invalid command");
|
||||
return;
|
||||
}
|
||||
synchronized (commandQueue) {
|
||||
// Add command to queue to avoid blocking
|
||||
commandQueue.offer(command);
|
||||
if (shouldReschedule) {
|
||||
shouldReschedule = false;
|
||||
reschedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void poll() {
|
||||
// Create controller instance
|
||||
try {
|
||||
IndegoController controller = new IndegoController(getConfig().get("username").toString(),
|
||||
getConfig().get("password").toString());
|
||||
// Connect to server
|
||||
controller.connect();
|
||||
// Query the device state
|
||||
DeviceStateInformation state = controller.getState();
|
||||
DeviceStatus statusWithMessage = DeviceStatus.decodeStatusCode(state.getState());
|
||||
int eshStatus = getEshStatusFromCommand(statusWithMessage.getAssociatedCommand());
|
||||
int mowed = state.getMowed();
|
||||
int error = state.getError();
|
||||
int statecode = state.getState();
|
||||
boolean ready = isReadyToMow(state.getState(), state.getError());
|
||||
DeviceCommand commandToSend = null;
|
||||
synchronized (commandQueue) {
|
||||
// Discard older commands
|
||||
while (!commandQueue.isEmpty()) {
|
||||
commandToSend = commandQueue.poll();
|
||||
}
|
||||
// For newer commands a new request is needed
|
||||
shouldReschedule = true;
|
||||
}
|
||||
if (commandToSend != null && verifyCommand(commandToSend, statusWithMessage.getAssociatedCommand(),
|
||||
state.getState(), error)) {
|
||||
logger.debug("Sending command...");
|
||||
updateState(TEXTUAL_STATE, UnDefType.UNDEF);
|
||||
controller.sendCommand(commandToSend);
|
||||
try {
|
||||
for (int i = 0; i < 30 && !Thread.interrupted(); i++) {
|
||||
DeviceStateInformation stateTmp = controller.getState();
|
||||
if (state.getState() != stateTmp.getState()) {
|
||||
state = stateTmp;
|
||||
statusWithMessage = DeviceStatus.decodeStatusCode(state.getState());
|
||||
eshStatus = getEshStatusFromCommand(statusWithMessage.getAssociatedCommand());
|
||||
mowed = state.getMowed();
|
||||
error = state.getError();
|
||||
statecode = state.getState();
|
||||
ready = isReadyToMow(state.getState(), state.getError());
|
||||
break;
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// Nothing to do here
|
||||
}
|
||||
}
|
||||
controller.disconnect();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateState(STATECODE, new DecimalType(statecode));
|
||||
updateState(READY, new DecimalType(ready ? 1 : 0));
|
||||
updateState(ERRORCODE, new DecimalType(error));
|
||||
updateState(MOWED, new PercentType(mowed));
|
||||
updateState(STATE, new DecimalType(eshStatus));
|
||||
updateState(TEXTUAL_STATE, new StringType(statusWithMessage.getMessage()));
|
||||
|
||||
} catch (IndegoAuthenticationException e) {
|
||||
String message = "The login credentials are wrong or another client connected to your Indego account";
|
||||
logger.warn(message, e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
|
||||
} catch (IndegoException e) {
|
||||
logger.warn("An error occurred", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReadyToMow(int statusCode, int error) {
|
||||
// I don´t know why bosch uses different state codes for the same state.
|
||||
return (statusCode == STATE_DOCKED_1 || statusCode == STATE_DOCKED_2 || statusCode == STATE_DOCKED_3
|
||||
|| statusCode == STATE_PAUSED || statusCode == STATE_IDLE_IN_LAWN) && error == 0;
|
||||
}
|
||||
|
||||
private boolean verifyCommand(DeviceCommand command, DeviceCommand state, int statusCode, int errorCode) {
|
||||
// Mower reported an error
|
||||
if (errorCode != 0) {
|
||||
logger.error("The mower reported an error.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Command is equal to current state
|
||||
if (command == state) {
|
||||
logger.debug("Command is equal to state");
|
||||
return false;
|
||||
}
|
||||
// Cant pause while the mower is docked
|
||||
if (command == DeviceCommand.PAUSE && state == DeviceCommand.RETURN) {
|
||||
logger.debug("Can´t pause the mower while it´s docked or docking");
|
||||
return false;
|
||||
}
|
||||
// Command means "MOW" but mower is not ready
|
||||
if (command == DeviceCommand.MOW && !isReadyToMow(statusCode, errorCode)) {
|
||||
logger.debug("The mower is not ready to mow in the moment");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int getEshStatusFromCommand(DeviceCommand command) {
|
||||
int eshStatus;
|
||||
switch (command) {
|
||||
case MOW:
|
||||
eshStatus = 1;
|
||||
break;
|
||||
case RETURN:
|
||||
eshStatus = 2;
|
||||
break;
|
||||
case PAUSE:
|
||||
eshStatus = 3;
|
||||
break;
|
||||
default:
|
||||
eshStatus = 0;
|
||||
}
|
||||
return eshStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
logger.debug("removing thing..");
|
||||
if (pollFuture != null) {
|
||||
pollFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void reschedule() {
|
||||
logger.debug("rescheduling");
|
||||
|
||||
if (pollFuture != null) {
|
||||
pollFuture.cancel(false);
|
||||
}
|
||||
|
||||
int refreshRate = ((BigDecimal) getConfig().get("refresh")).intValue();
|
||||
pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshRate, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
|
||||
super.handleConfigurationUpdate(configurationParameters);
|
||||
reschedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
reschedule();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="boschindego" 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>BoschIndego Binding</name>
|
||||
<description>This is the binding for Bosch Indego Connect lawn mowers.</description>
|
||||
<author>Jonas Fleck</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="boschindego"
|
||||
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="indego">
|
||||
<label>Bosch Indego</label>
|
||||
<description>Indego which supports the connect feature.</description>
|
||||
<channels>
|
||||
<channel id="state" typeId="state"/>
|
||||
<channel id="textualstate" typeId="textualstate"/>
|
||||
<channel id="errorcode" typeId="errorcode"/>
|
||||
<channel id="statecode" typeId="statecode"/>
|
||||
<channel id="mowed" typeId="mowed"/>
|
||||
<channel id="ready" typeId="ready"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="username" type="text" required="true">
|
||||
<label>Username</label>
|
||||
<description>Username for the Bosch Indego account.</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password for the Bosch Indego account.</description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="60">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in seconds.</description>
|
||||
<default>180</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="state">
|
||||
<item-type>Number</item-type>
|
||||
<label>Numeric State</label>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="1">Mow</option>
|
||||
<option value="2">Charge/Dock</option>
|
||||
<option value="3">Pause</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="errorcode" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>Error Code</label>
|
||||
<description>0 = no error</description>
|
||||
<state readOnly="false"></state>
|
||||
</channel-type>
|
||||
<channel-type id="statecode" advanced="true">
|
||||
<item-type>Number</item-type>
|
||||
<label>State Code</label>
|
||||
<description>API-code of the Indego state</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">Reading status</option>
|
||||
<option value="257">Charging</option>
|
||||
<option value="258">Docked</option>
|
||||
<option value="259">Docked - Software update</option>
|
||||
<option value="260">Docked</option>
|
||||
<option value="261">Docked</option>
|
||||
<option value="262">Docked - Loading map</option>
|
||||
<option value="263">Docked - Saving map</option>
|
||||
<option value="512">Mowing</option>
|
||||
<option value="514">Relocalising</option>
|
||||
<option value="515">Loading map</option>
|
||||
<option value="516">Learning lawn</option>
|
||||
<option value="517">Paused</option>
|
||||
<option value="518">Border cut</option>
|
||||
<option value="519">Idle in lawn</option>
|
||||
<option value="769">Returning to Dock</option>
|
||||
<option value="770">Returning to Dock</option>
|
||||
<option value="771">Returning to Dock - Battery low</option>
|
||||
<option value="772">Returning to Dock - Calendar timeslot ended</option>
|
||||
<option value="773">Returning to Dock - Battery temp range</option>
|
||||
<option value="774">Returning to Dock</option>
|
||||
<option value="775">Returning to Dock - Lawn complete</option>
|
||||
<option value="775">Returning to Dock - Relocalising</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="textualstate">
|
||||
<item-type>String</item-type>
|
||||
<label>Textual State</label>
|
||||
<description></description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="mowed">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Cut Grass</label>
|
||||
<description></description>
|
||||
<state readOnly="true" pattern="%d %%"></state>
|
||||
</channel-type>
|
||||
<channel-type id="ready">
|
||||
<item-type>Number</item-type>
|
||||
<label>Ready</label>
|
||||
<description>Indicates if mower is ready to mow</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">not ready</option>
|
||||
<option value="1">ready</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user