added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
50
bundles/org.openhab.binding.kvv/.classpath
Normal file
50
bundles/org.openhab.binding.kvv/.classpath
Normal 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>
|
||||
23
bundles/org.openhab.binding.kvv/.project
Normal file
23
bundles/org.openhab.binding.kvv/.project
Normal 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>
|
||||
13
bundles/org.openhab.binding.kvv/NOTICE
Normal file
13
bundles/org.openhab.binding.kvv/NOTICE
Normal 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
|
||||
110
bundles/org.openhab.binding.kvv/README.md
Normal file
110
bundles/org.openhab.binding.kvv/README.md
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
17
bundles/org.openhab.binding.kvv/pom.xml
Normal file
17
bundles/org.openhab.binding.kvv/pom.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = "";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 = "";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user