added migrated 2.x add-ons

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

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.kvv</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,110 @@
# KVV Binding
Adds support for the public API of Karlsruher Verkehrsverbund (public transport system in Karlsruhe, Germany).
Enables the user to show the latest departure times for specific street car stops in openHAB.
## Supported Things
Every street car stop is represented by one thing.
Each thing contains channels for the information referred to the next n trains.
This includes the name of the train, the final destination and the estimated time available (eta).
## Thing Configuration
Since every stop is represented by a KVV-provided id, this id has to be figured out via an API call.
### Example Call for Stop 'Karlsruhe Volkswohnung'
```bash
# Request
curl https://live.kvv.de/webapp/stops/byname/Volkswohnung\?key\=[APIKEY]
# Response
{"stops":[{"id":"de:8212:72","name":"Karlsruhe Volkswohnung","lat":49.00381654,"lon":8.40393026}]}
```
## Channel Configuration
Each stop automatically creates a set of channels depending on the configuration.
If `maxTrains` is set to three, each stop creates three sets of channels.
Each set consists of the following three channels:
| Channel | Type | Description |
| :--------------------------------------------------------- | :----- | :------------------------------------------------------------ |
| kvv:stop:${bridgeId}:${stopId}:train${trainId}-name | String | Name of the line, e.g. _S5_ |
| kvv:stop:${bridgeId}:${stopId}:train${trainId}-destination | String | Name of the stop the train is heading to |
| kvv:stop:${bridgeId}:${stopId}:train${trainId}-eta | String | Duration until the train arrives. Can be relative or absolute |
## Full Example
### Things
```things
Bridge kvv:bridge:1 "Bridge" @ "Wohnzimmer" [ maxTrains="3", updateInterval="10", apiKey="" ] {
stop gottesauerplatz "Gottesauer Platz/BGV" [ stopId="de:8212:6" ]
}
```
### Items
```items
String kvv_gottesauerplatz_train0_name "Name [%s]" {channel="kvv:stop:1:gottesauerplatz:train0-name"}
String kvv_gottesauerplatz_train0_destination "Destination [%s]" {channel="kvv:stop:1:gottesauerplatz:train0-destination"}
String kvv_gottesauerplatz_train0_eta "ETA [%s]" {channel="kvv:stop:1:gottesauerplatz:train0-eta"}
String kvv_gottesauerplatz_train1_name "Name [%s]" {channel="kvv:stop:1:gottesauerplatz:train1-name"}
String kvv_gottesauerplatz_train1_destination "Destination [%s]" {channel="kvv:stop:1:gottesauerplatz:train1-destination"}
String kvv_gottesauerplatz_train1_eta "ETA [%s]" {channel="kvv:stop:1:gottesauerplatz:train1-eta"}
String kvv_gottesauerplatz_train2_name "Name [%s]" {channel="kvv:stop:1:gottesauerplatz:train2-name"}
String kvv_gottesauerplatz_train2_destination "Destination [%s]" {channel="kvv:stop:1:gottesauerplatz:train2-destination"}
String kvv_gottesauerplatz_train2_eta "ETA [%s]" {channel="kvv:stop:1:gottesauerplatz:train2-eta"}
```
### Template for HABPanel
```html
<style>
.kvvtable > tbody > tr > td {padding: 3px 20px; text-align: left;}
</style>
<table class="kvvtable">
<tr>
<td>{{itemValue('kvv_gottesauerplatz_train0_name')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train0_destination')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train0_eta')}}</td>
</tr>
<tr>
<td>{{itemValue('kvv_gottesauerplatz_train1_name')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train1_destination')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train1_eta')}}</td>
</tr><tr>
<td>{{itemValue('kvv_gottesauerplatz_train2_name')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train2_destination')}}</td>
<td>{{itemValue('kvv_gottesauerplatz_train2_eta')}}</td>
</tr>
</table>
```
### Sitemap for Basic UI
```sitemap
sitemap kvv label="KVV" {
Frame label="Gottesauer Platz/BGV" {
Group item=kvv_gottesauerplatz_train0_destination label="Train #1" {
Text item=kvv_gottesauerplatz_train0_name
Text item=kvv_gottesauerplatz_train0_destination
Text item=kvv_gottesauerplatz_train0_eta
}
Group item=kvv_gottesauerplatz_train1_destination label="Train #2" {
Text item=kvv_gottesauerplatz_train1_name
Text item=kvv_gottesauerplatz_train1_destination
Text item=kvv_gottesauerplatz_train1_eta
}
Group item=kvv_gottesauerplatz_train2_destination label="Train #3" {
Text item=kvv_gottesauerplatz_train2_name
Text item=kvv_gottesauerplatz_train2_destination
Text item=kvv_gottesauerplatz_train2_eta
}
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.kvv</artifactId>
<name>openHAB Add-ons :: Bundles :: KVV Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.kvv-${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-kvv" description="KVV Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.kvv/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,76 @@
/**
* 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.kvv.internal;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Represents the result of a call to the KVV api to fetch the next departures at a specific stop.
*
* @author Maximilian Hess - Initial contribution
*
*/
@NonNullByDefault
public class DepartureResult {
public String stopName;
public List<Departure> departures;
public DepartureResult() {
this.stopName = "";
this.departures = new ArrayList<Departure>();
}
@Override
public String toString() {
return "DepartureResult [stopName=" + stopName + ", departures=" + departures + "]";
}
/**
* Encapsulates a single {@link Departure}. Most important are 'route', 'destination' and 'destination'.
*
* @author Maximilian Hess - Initial contribution
*
*/
@NonNullByDefault
public static class Departure {
public String route = "";
public String destination = "";
public String direction = "";
public String time = "";
public String vehicleType = "";
public boolean lowfloor;
public boolean realtime;
public int traction;
public String stopPosition = "";
@Override
public String toString() {
final String timePrefix = (this.time.endsWith("min")) ? " in " : " at ";
return "Route " + this.route + timePrefix + this.time + " heading to " + this.destination;
}
}
}

View File

@@ -0,0 +1,49 @@
/**
* 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.kvv.internal;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link KVVBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Maximilian Hess - Initial contribution
*/
@NonNullByDefault
public class KVVBindingConstants {
private static final String BINDING_ID = "kvv";
/** the thing type of the bridges */
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
/** the thing type of the stop */
public static final ThingTypeUID THING_TYPE_STOP = new ThingTypeUID(BINDING_ID, "stop");
/** all of the supported types */
public static final List<ThingTypeUID> SUPPORTED_THING_TYPES = Arrays.asList(THING_TYPE_BRIDGE, THING_TYPE_STOP);
/** URL of the KVV API */
public static final String API_URL = "https://live.kvv.de/webapp";
/** timeout for API calls in seconds */
public static final int TIMEOUT_IN_SECONDS = 10;
/** default value to initialize the API cache */
public static final int CACHE_DEFAULT_UPDATEINTERVAL = 10;
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.kvv.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Holds information parameters.
*
* @author Maximilian Hess - Initial contribution
*
*/
@NonNullByDefault
public class KVVBridgeConfig {
/** maximum number of traines being queried */
public int maxTrains;
/** the update interval in seconds */
public int updateInterval;
/** API key of the KVV API */
public String apiKey;
public KVVBridgeConfig() {
this.maxTrains = 0;
this.updateInterval = 10;
this.apiKey = "";
}
}

View File

@@ -0,0 +1,211 @@
/**
* 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.kvv.internal;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* KVVBridgeHandler encapsulates the communication with the KVV API.
*
* @author Maximilian Hess - Initial contribution
*/
@NonNullByDefault
public class KVVBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(KVVBridgeHandler.class);
private final Cache cache;
private KVVBridgeConfig config;
private boolean wasOffline;
public KVVBridgeHandler(final Bridge bridge) {
super(bridge);
this.config = new KVVBridgeConfig();
this.cache = new Cache();
this.wasOffline = false;
}
public KVVBridgeConfig getBridgeConfig() {
return this.config;
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
this.config = getConfigAs(KVVBridgeConfig.class);
if (this.config.apiKey.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Failed to get bridge configuration");
return;
}
updateStatus(ThingStatus.ONLINE);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// There is nothing to handle in the bridge handler
}
/**
* Returns the latest {@link DepartureResult}. Returns {@code null} if the result could not be retrieved.
*
* @return the latest {@link DepartureResult}.
*/
public synchronized @Nullable DepartureResult queryKVV(final KVVStopConfig stopConfig) {
// is there an up-to-date value in the cache?
final DepartureResult cr = this.cache.get(stopConfig.stopId);
if (cr != null) {
return cr;
}
final String url = KVVBindingConstants.API_URL + "/departures/bystop/" + stopConfig.stopId + "?key="
+ config.apiKey + "&maxInfos=" + config.maxTrains;
String data;
try {
data = HttpUtil.executeUrl("GET", url, KVVBindingConstants.TIMEOUT_IN_SECONDS * 1000);
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Failed to connect to KVV API");
logger.debug("Failed to get departures from '{}'", url, e);
this.wasOffline = true;
return null;
}
DepartureResult result;
try {
result = new Gson().fromJson(data, DepartureResult.class);
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Failed to connect to KVV API");
logger.debug("Failed to parse departure data", e);
logger.debug("Server returned '{}'", data);
this.wasOffline = true;
return null;
}
if (this.wasOffline) {
updateStatus(ThingStatus.ONLINE);
}
this.cache.update(stopConfig.stopId, result);
return result;
}
@NonNullByDefault
public static class Cache {
private int updateInterval;
private final Map<String, CacheLine> cache;
/**
* Creates a new @{link Cache}.
*
* @param updateInterval the @{code updateInterval}
*/
public Cache() {
this.updateInterval = KVVBindingConstants.CACHE_DEFAULT_UPDATEINTERVAL;
this.cache = new HashMap<String, CacheLine>();
}
/*
* Updates the @{code updateInterval}.
*
* @param updateInterval the new @{code updateInterval}
*/
public void setUpdateInterval(final int updateInterval) {
this.updateInterval = updateInterval;
}
/**
* Returns the result of the latest API call for a given stop. Returns @{code null} if the latest result is
* out dated or the @{link CacheLine} does not exist. Not distinguishing between those two cases is sufficient,
* because it leads to the same handling of @{link KVVBridgeHandler}.
*
* @param stopId
* @return the result of the latest API call for a given stop.
*/
@Nullable
public DepartureResult get(final String stopId) {
if (!this.cache.containsKey(stopId)) {
return null;
}
final CacheLine cl = this.cache.get(stopId);
if (cl.getEvictAfter().before(new Date())) {
return null;
}
return cl.getPayload();
}
public void update(final String stopId, final DepartureResult payload) {
if (!this.cache.containsKey(stopId)) {
this.cache.put(stopId, new CacheLine(payload, new Date()));
}
final CacheLine cl = this.cache.get(stopId);
// the eviction time is calculated by adding an offset of 60 percent of the regular update interval of
// the bridge handler
cl.update(payload, new Date(System.currentTimeMillis() + (long) (0.6 * this.updateInterval * 1000)));
}
}
@NonNullByDefault
public static class CacheLine {
private Date evictAfter;
private DepartureResult payload;
public CacheLine(final DepartureResult payload, final Date evictAfter) {
this.payload = payload;
this.evictAfter = evictAfter;
}
public Date getEvictAfter() {
return this.evictAfter;
}
public DepartureResult getPayload() {
return this.payload;
}
public void update(final DepartureResult payload, final Date evictAfter) {
this.payload = payload;
this.evictAfter = evictAfter;
}
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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.kvv.internal;
import static org.openhab.binding.kvv.internal.KVVBindingConstants.THING_TYPE_BRIDGE;
import static org.openhab.binding.kvv.internal.KVVBindingConstants.THING_TYPE_STOP;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Bridge;
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 KVVHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Maximilian Hess - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.kvv", service = ThingHandlerFactory.class)
public class KVVHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(final ThingTypeUID thingTypeUID) {
return KVVBindingConstants.SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(final Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_BRIDGE.equals(thingTypeUID)) {
return new KVVBridgeHandler((Bridge) thing);
} else if (THING_TYPE_STOP.equals(thingTypeUID)) {
return new KVVStopHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.kvv.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Holds information parameters.
*
* @author Maximilian Hess - Initial contribution
*
*/
@NonNullByDefault
public class KVVStopConfig {
/** the id of the stop */
public String stopId;
public KVVStopConfig() {
this.stopId = "";
}
}

View File

@@ -0,0 +1,185 @@
/**
* 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.kvv.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
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.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
/**
* KVVStopHandler represents a stop and holds information about the trains
* which will arrive soon.
*
* @author Maximilian Hess - Initial contribution
*/
@NonNullByDefault
public class KVVStopHandler extends BaseThingHandler {
@Nullable
private ScheduledFuture<?> pollingJob;
@Nullable
private KVVBridgeHandler bridgeHandler;
private KVVStopConfig config;
private boolean wasOffline;
public KVVStopHandler(final Thing thing) {
super(thing);
this.config = new KVVStopConfig();
this.wasOffline = false;
}
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
init(true);
updateStatus(ThingStatus.ONLINE);
}
private synchronized void init(final boolean createChannels) {
this.config = getConfigAs(KVVStopConfig.class);
if (config.stopId.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Failed to get stop configuration");
return;
}
final Bridge bridge = getBridge();
if (bridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Failed to get bridge");
return;
}
final KVVBridgeHandler bridgeHandler = (KVVBridgeHandler) bridge.getHandler();
if (bridgeHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_MISSING_ERROR, "Failed to get bridge handler");
return;
}
// create channels
if (createChannels) {
final ChannelTypeUID nameType = new ChannelTypeUID(this.thing.getBridgeUID().getBindingId(), "name");
final ChannelTypeUID destType = new ChannelTypeUID(this.thing.getBridgeUID().getBindingId(), "destination");
final ChannelTypeUID etaType = new ChannelTypeUID(this.thing.getBridgeUID().getBindingId(), "eta");
final List<Channel> channels = new ArrayList<Channel>();
for (int i = 0; i < bridgeHandler.getBridgeConfig().maxTrains; i++) {
channels.add(ChannelBuilder.create(new ChannelUID(this.thing.getUID(), "train" + i + "-name"), "String")
.withType(nameType).build());
channels.add(ChannelBuilder
.create(new ChannelUID(this.thing.getUID(), "train" + i + "-destination"), "String")
.withType(destType).build());
channels.add(ChannelBuilder.create(new ChannelUID(this.thing.getUID(), "train" + i + "-eta"), "String")
.withType(etaType).build());
}
this.updateThing(this.editThing().withChannels(channels).build());
}
this.pollingJob = this.scheduler.scheduleWithFixedDelay(new UpdateTask(bridgeHandler, this.config), 0,
bridgeHandler.getBridgeConfig().updateInterval, TimeUnit.SECONDS);
this.bridgeHandler = bridgeHandler;
}
@Override
public void dispose() {
if (this.pollingJob != null) {
this.pollingJob.cancel(false);
}
}
/**
* Updates the local list of departures.
*
* @param departures the new list of departures
*/
private synchronized void setDepartures(final DepartureResult departures, final int maxTrains) {
int i = 0;
for (; i < departures.departures.size(); i++) {
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-name"),
new StringType(departures.departures.get(i).route));
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-destination"),
new StringType(departures.departures.get(i).destination));
String eta = departures.departures.get(i).time;
if (eta.equals("0")) {
eta += " min";
}
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-eta"), new StringType(eta));
}
for (; i < maxTrains; i++) {
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-name"), StringType.EMPTY);
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-destination"), StringType.EMPTY);
this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-eta"), StringType.EMPTY);
}
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
if (command == RefreshType.REFRESH) {
init(false);
}
}
/**
* Holds a single {@link TimerTask} to fetch the latest departure data.
*
* @author Maximilian Hess - Initial contribution
*
*/
@NonNullByDefault
public class UpdateTask extends TimerTask {
private final KVVBridgeHandler bridgeHandler;
private final KVVStopConfig stopConfig;
public UpdateTask(final KVVBridgeHandler bridgeHandler, final KVVStopConfig stopConfig) {
this.bridgeHandler = bridgeHandler;
this.stopConfig = stopConfig;
}
@Override
public void run() {
final DepartureResult departures = this.bridgeHandler.queryKVV(this.stopConfig);
if (departures == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Failed to connect to KVV API");
} else {
if (wasOffline) {
updateStatus(ThingStatus.ONLINE);
}
setDepartures(departures, this.bridgeHandler.getBridgeConfig().maxTrains);
}
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="kvv" 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>KVV Binding</name>
<description>This is the binding for KVV.</description>
<author>Maximilian Hess</author>
</binding:binding>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="kvv" 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">
<bridge-type id="bridge">
<label>KVV Bridge</label>
<description>The KVV bridge connects to the KVV API.</description>
<config-description>
<parameter name="maxTrains" type="integer" required="true" min="0" max="127">
<label>Maximum Trains Listed</label>
<description>Maximum number of trains listed.</description>
</parameter>
<parameter name="updateInterval" type="integer" required="true" min="1">
<label>Update Interval</label>
<description>Update interval in seconds.</description>
</parameter>
<parameter name="apiKey" type="text" required="true">
<label>API key</label>
<description>API key of the KVV API</description>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="kvv" 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">
<channel-type id="name">
<item-type>String</item-type>
<label>Train Line Name</label>
<description>Name of the Train Line</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="destination">
<item-type>String</item-type>
<label>Destination of the train</label>
<description>Name of the Train Line</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="eta">
<item-type>String</item-type>
<label>Estimated Time Available</label>
<description>Time at which the train arrives</description>
<state readOnly="true"/>
</channel-type>
<channel-group-type id="train">
<label>KVV Train</label>
<description>This is a single KVV train</description>
<channels>
<channel id="name" typeId="name"/>
<channel id="destination" typeId="destination"/>
<channel id="eta" typeId="eta"/>
</channels>
</channel-group-type>
<thing-type id="stop">
<label>KVV Stop</label>
<description>Train stop for KVV Binding.</description>
<config-description>
<parameter name="stopId" type="text">
<label>Stop ID</label>
<description>ID of the train stop.</description>
<required>true</required>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>