]> git.basschouten.com Git - openhab-addons.git/commitdiff
[KVV] Replace underlying API (#13186)
authorMaximilian Heß <mail@ne0h.de>
Sun, 25 Sep 2022 12:29:10 +0000 (14:29 +0200)
committerGitHub <noreply@github.com>
Sun, 25 Sep 2022 12:29:10 +0000 (14:29 +0200)
* [KVV] Replace underlying API

The underlying API does no longer work so it is replace. An API key is no longer necessary.
Users need to replace the `stopId` in there station thing configuration. See README.md for details.

Signed-off-by: Maximilian Hess <mail@ne0h.de>
Co-authored-by: Laurent Garnier <lg.hc@free.fr>
bundles/org.openhab.binding.kvv/README.md
bundles/org.openhab.binding.kvv/src/main/java/org/openhab/binding/kvv/internal/DepartureResult.java
bundles/org.openhab.binding.kvv/src/main/java/org/openhab/binding/kvv/internal/KVVBindingConstants.java
bundles/org.openhab.binding.kvv/src/main/java/org/openhab/binding/kvv/internal/KVVBridgeConfig.java
bundles/org.openhab.binding.kvv/src/main/java/org/openhab/binding/kvv/internal/KVVBridgeHandler.java
bundles/org.openhab.binding.kvv/src/main/java/org/openhab/binding/kvv/internal/KVVStopHandler.java
bundles/org.openhab.binding.kvv/src/main/resources/OH-INF/i18n/kvv.properties
bundles/org.openhab.binding.kvv/src/main/resources/OH-INF/thing/bridge.xml

index 4084761a56db0a34cc40c961a5cf85355b9e8470..612d0528573feb15e00ef1ec6a2a5520b5cbb8a1 100644 (file)
@@ -13,14 +13,38 @@ This includes the name of the train, the final destination and the estimated tim
 
 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'
+### Example Call for Stop 'Gottesauer Platz/BGV'
 
 ```bash
-# Request
-curl https://live.kvv.de/webapp/stops/byname/Volkswohnung\?key\=[APIKEY]
+export QUERY="gottesauer"
+curl https://www.kvv.de/tunnelEfaDirect.php?action=XSLT_STOPFINDER_REQUEST&name_sf=${QUERY}&outputFormat=JSON&type_sf=any
+```
 
-# Response
-{"stops":[{"id":"de:8212:72","name":"Karlsruhe Volkswohnung","lat":49.00381654,"lon":8.40393026}]}
+The exact `id` may be extracted from the JSON-encoded reponse. E.g.
+
+```json
+"points": [
+{
+    "usage": "sf",
+    "type": "any",
+    "name": "Karlsruhe, Gottesauer Platz/BGV",
+    "stateless": "7000006",
+    "anyType": "stop",
+    "sort": "2",
+    "quality": "949",
+    "best": "0",
+    "object": "Gottesauer Platz/BGV",
+    "mainLoc": "Karlsruhe",
+    "modes": "1,4,5",
+    "ref": {
+        "id": "7000006",
+        "gid": "de:08212:6",
+        "omc": "8212000",
+        "placeID": "15",
+        "place": "Karlsruhe",
+        "coords": "937855.00000,5723868.00000"
+    }
+}
 ```
 
 ## Channel Configuration
@@ -40,8 +64,8 @@ Each set consists of the following three channels:
 ### Things
 
 ```things
-Bridge kvv:bridge:1 "Bridge" @ "Wohnzimmer" [ maxTrains="3", updateInterval="10", apiKey="" ] {
-    stop gottesauerplatz        "Gottesauer Platz/BGV"      [ stopId="de:8212:6" ]
+Bridge kvv:bridge:1 "Bridge" @ "Wohnzimmer" [ maxTrains="3", updateInterval="10" ] {
+    stop gottesauerplatz        "Gottesauer Platz/BGV"      [ stopId="7000006" ]
 }
 ```
 
index ddaab780cc346e2e6b7edd298ab0e03ae939b46d..75243d854b71561fb9a438aea1d384f40049d707 100644 (file)
@@ -17,8 +17,10 @@ import java.util.List;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 
+import com.google.gson.annotations.SerializedName;
+
 /**
- * Represents the result of a call to the KVV api to fetch the next departures at a specific stop.
+ * Represents the result of a call to the KVV API to fetch the next departures at a specific stop.
  *
  * @author Maximilian Hess - Initial contribution
  *
@@ -26,18 +28,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class DepartureResult {
 
-    public String stopName;
-
-    public List<Departure> departures;
-
-    public DepartureResult() {
-        this.stopName = "";
-        this.departures = new ArrayList<Departure>();
-    }
+    @SerializedName(value = "departureList")
+    public List<Departure> departures = new ArrayList<Departure>();
 
     @Override
     public String toString() {
-        return "DepartureResult [stopName=" + stopName + ", departures=" + departures + "]";
+        return "DepartureResult [departures=" + departures + "]";
     }
 
     /**
@@ -49,28 +45,29 @@ public class DepartureResult {
     @NonNullByDefault
     public static class Departure {
 
-        public String route = "";
-
-        public String destination = "";
-
-        public String direction = "";
-
-        public String time = "";
+        @SerializedName(value = "servingLine")
+        public Route route = new Route();
 
-        public String vehicleType = "";
+        @SerializedName(value = "countdown")
+        public String eta = "";
 
-        public boolean lowfloor;
+        @Override
+        public String toString() {
+            return "Departure [" + route + ", eta=" + eta + "]";
+        }
+    }
 
-        public boolean realtime;
+    @NonNullByDefault
+    public static class Route {
 
-        public int traction;
+        @SerializedName(value = "number")
+        public String name = "";
 
-        public String stopPosition = "";
+        public String direction = "";
 
         @Override
         public String toString() {
-            final String timePrefix = (this.time.endsWith("min")) ? " in " : " at ";
-            return "Route " + this.route + timePrefix + this.time + " heading to " + this.destination;
+            return "name=" + name + ", direction=" + direction;
         }
     }
 }
index ad7885c773c92a1de50062bd8868ebf58e97181c..b20f27b4741a0c1c4d5b21c26bfe95f5e3fa54a2 100644 (file)
@@ -39,7 +39,7 @@ public class KVVBindingConstants {
     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";
+    public static final String API_FORMAT = "https://projekte.kvv-efa.de/sl3-alone/XSLT_DM_REQUEST?outputFormat=JSON&coordOutputFormat=WGS84%%5Bdd.ddddd%%5D&depType=stopEvents&locationServerActive=1&mode=direct&name_dm=%s&type_dm=stop&useOnlyStops=1&useRealtime=1&limit=%d";
 
     /** timeout for API calls in seconds */
     public static final int TIMEOUT_IN_SECONDS = 10;
index 4afb93f2491870ded67aedbac068cf635262c000..3bed729c65a9fc21a2bfada9199f7819b8b7aeef 100644 (file)
@@ -23,18 +23,14 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 @NonNullByDefault
 public class KVVBridgeConfig {
 
-    /** maximum number of traines being queried */
+    /** maximum number of trains 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.maxTrains = 1;
         this.updateInterval = 10;
-        this.apiKey = "";
     }
 }
index 84e572b45db58fa498c3a01aebf950cb4c28ef6b..878478acdd13610e1c23ba2b73f398a957232ebd 100644 (file)
@@ -61,15 +61,7 @@ public class KVVBridgeHandler extends BaseBridgeHandler {
 
     @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);
     }
 
@@ -91,8 +83,7 @@ public class KVVBridgeHandler extends BaseBridgeHandler {
             return cr;
         }
 
-        final String url = KVVBindingConstants.API_URL + "/departures/bystop/" + stopConfig.stopId + "?key="
-                + config.apiKey + "&maxInfos=" + config.maxTrains;
+        final String url = String.format(KVVBindingConstants.API_FORMAT, stopConfig.stopId, config.maxTrains);
 
         String data;
         try {
index d86e488bc71eff6652ec999cac702eadcd75490d..4eb1a5f50cb05cd85d3deadcae5aa7cda82f1443 100644 (file)
@@ -32,6 +32,8 @@ 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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * KVVStopHandler represents a stop and holds information about the trains
@@ -42,6 +44,8 @@ import org.openhab.core.types.RefreshType;
 @NonNullByDefault
 public class KVVStopHandler extends BaseThingHandler {
 
+    private final Logger logger = LoggerFactory.getLogger(KVVStopHandler.class);
+
     @Nullable
     private ScheduledFuture<?> pollingJob;
 
@@ -91,18 +95,25 @@ public class KVVStopHandler extends BaseThingHandler {
             final ChannelTypeUID destType = new ChannelTypeUID(this.thing.getBridgeUID().getBindingId(), "destination");
             final ChannelTypeUID etaType = new ChannelTypeUID(this.thing.getBridgeUID().getBindingId(), "eta");
 
+            if (bridgeHandler.getBridgeConfig().maxTrains == 0) {
+                logger.warn("maxTrains is '0', not creating any channels");
+            }
+
             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());
+                ChannelUID c = new ChannelUID(this.thing.getUID(), "train" + i + "-name");
+                channels.add(ChannelBuilder.create(c, "String").withType(nameType).build());
+                logger.debug("Created channel {}", c);
+
+                c = new ChannelUID(this.thing.getUID(), "train" + i + "-destination");
+                channels.add(ChannelBuilder.create(c, "String").withType(destType).build());
+                logger.debug("Created channel {}", c);
+
+                c = new ChannelUID(this.thing.getUID(), "train" + i + "-eta");
+                channels.add(ChannelBuilder.create(c, "String").withType(etaType).build());
+                logger.debug("Created channel {}", c);
             }
             this.updateThing(this.editThing().withChannels(channels).build());
-
         }
 
         this.pollingJob = this.scheduler.scheduleWithFixedDelay(new UpdateTask(bridgeHandler, this.config), 0,
@@ -126,16 +137,19 @@ public class KVVStopHandler extends BaseThingHandler {
     private synchronized void setDepartures(final DepartureResult departures, final int maxTrains) {
         int i = 0;
         for (; i < departures.departures.size(); i++) {
+            final DepartureResult.Departure departure = departures.departures.get(i);
+
             this.updateState(new ChannelUID(this.thing.getUID(), "train" + i + "-name"),
-                    new StringType(departures.departures.get(i).route));
+                    new StringType(departure.route.name));
             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")) {
+                    new StringType(departure.route.direction));
+            String eta = departure.eta;
+            if ("0".equals(eta)) {
                 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);
index 524ec04d876b35c20a1874bca1ca4e3e205f426a..9f49f6060e9e19f92f67a6d03f3f18c2d8464b93 100644 (file)
@@ -12,9 +12,7 @@ thing-type.kvv.stop.description = Train stop for KVV Binding.
 
 # thing types config
 
-thing-type.config.kvv.bridge.apiKey.label = API key
-thing-type.config.kvv.bridge.apiKey.description = API key of the KVV API
-thing-type.config.kvv.bridge.maxTrains.label = Maximum Trains Listed
+thing-type.config.kvv.bridge.maxTrains.label = Maximum Trains listed
 thing-type.config.kvv.bridge.maxTrains.description = Maximum number of trains listed.
 thing-type.config.kvv.bridge.updateInterval.label = Update Interval
 thing-type.config.kvv.bridge.updateInterval.description = Update interval in seconds.
index 371ccf0bd962d6c12991d070bcbdeb9a4cd04661..ea0abae9cea4d145ea49e1b407a869c1f5af04b3 100644 (file)
@@ -8,17 +8,15 @@
                <description>The KVV bridge connects to the KVV API.</description>
 
                <config-description>
-                       <parameter name="maxTrains" type="integer" required="true" min="0" max="127">
+                       <parameter name="maxTrains" type="integer" min="1" max="127">
                                <label>Maximum Trains Listed</label>
                                <description>Maximum number of trains listed.</description>
+                               <default>1</default>
                        </parameter>
-                       <parameter name="updateInterval" type="integer" required="true" min="1">
+                       <parameter name="updateInterval" type="integer" 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>
+                               <default>10</default>
                        </parameter>
                </config-description>
        </bridge-type>