Nextride
Wann fährt die nächste Bahn?
Nextride 2.0?
Im alten Space an der Sudbrackstraße hatten wir ein Display der nächsten Nahverkehr-Abfahrzeiten (s.u.), an der Blomestraße fehlt so etwas leider bisher. Der Link oben wurde mit dem VRR-Haltestellenmonitor erstellt.
Der eigentliche API-Endpunkt hinter dem Haltestellenmonitor ist "https://haltestellenmonitor.vrr.de/backend/api/stations/table, darauf aufbauend könnte ein neues nextride entstehen.
Als neue Anzeigemöglichkeit könnte ich mir zB. -- energiesparend :) -- einen Arduino mit eInk-Display vorstellen?
VRR API
Dokumentation zu diesem API habe ich beim VRR auf die Schnelle nicht gefunden, es gibt dort zwar einen "Open Data" Bereich, aber dort scheinen nur Download-Datensätze angeboten zu werden, aber keine Live-Daten. Daher ist alles was hier folgt Ergebnis von Reverse Engineering.
Request
Der Haltestellenmonitor schickt die Haltestellendaten als POST-Request, und die POST-Parameter für eine Haltestelle sind konstant, zB. für die Stadtbahn-Haltestelle Ziegelstraße:
table%5Bdeparture%5D%5BstationId%5D=23005614&table%5Bdeparture%5D%5BstationName%5D=Bielefeld+Ziegelstra%C3%9Fe&table%5Bdeparture%5D%5BplatformVisibility%5D=1&table%5Bdeparture%5D%5Btransport%5D=0%2C1%2C2%2C3%2C4%2C15%2C6&table%5Bdeparture%5D%5BuseAllLines%5D=1&table%5Bdeparture%5D%5BlinesFilter%5D=&table%5Bdeparture%5D%5BoptimizedForStation%5D=0&table%5Bdeparture%5D%5BrowCount%5D=6&table%5Bdeparture%5D%5BrefreshInterval%5D=60&table%5Bdeparture%5D%5Bdistance%5D=0&table%5Bdeparture%5D%5Bmarquee%5D=-1&table%5BsortBy%5D=0
oder netter formatiert:
table[departure][stationId] "23005614" table[departure][stationName] "Bielefeld+Ziegelstraße" table[departure][platformVisibility] "1" table[departure][transport] "0,1,2,3,4,15,6" table[departure][useAllLines] "1" table[departure][linesFilter] "" table[departure][optimizedForStation] "0" table[departure][rowCount] "6" table[departure][refreshInterval] "60" table[departure][distance] "0" table[departure][marquee] "-1" table[sortBy] "0"
Response
Die Antwort kommt als JSON-Dokument zurück, damit lässt sich gut weiterarbeiten:
{ "departureData":[ { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Altenhagen", "directionCode":"H", "route":"Bielefeld, Altenhagen", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"03", "orgDay":"23", "orgHour":"9", "orgMinute":"00", "countdown":"4", "platform":"", "delay":"3", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682233380", "orgFullTime":"1682233200", "supplement":"", "key":"231" }, { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Sieker", "directionCode":"R", "route":"Bielefeld, Sieker", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"04", "orgDay":"23", "orgHour":"9", "orgMinute":"04", "countdown":"5", "platform":"", "delay":"0", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682233440", "orgFullTime":"1682233440", "supplement":"", "key":"38" }, { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Altenhagen", "directionCode":"H", "route":"Bielefeld, Altenhagen", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"15", "orgDay":"23", "orgHour":"9", "orgMinute":"15", "countdown":"16", "platform":"", "delay":"0", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682234100", "orgFullTime":"1682234100", "supplement":"", "key":"234" }, { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Sieker", "directionCode":"R", "route":"Bielefeld, Sieker", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"19", "orgDay":"23", "orgHour":"9", "orgMinute":"19", "countdown":"20", "platform":"", "delay":"0", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682234340", "orgFullTime":"1682234340", "supplement":"", "key":"40" }, { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Altenhagen", "directionCode":"H", "route":"Bielefeld, Altenhagen", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"30", "orgDay":"23", "orgHour":"9", "orgMinute":"30", "countdown":"31", "platform":"", "delay":"0", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682235000", "orgFullTime":"1682235000", "supplement":"", "key":"236" }, { "name":"2", "lineNumber":"2", "lineCode":"31002", "subname":"Stadtbahn", "direction":"Bielefeld, Sieker", "directionCode":"R", "route":"Bielefeld, Sieker", "type":4, "day":"23", "month":"4", "year":"2023", "hour":"9", "minute":"34", "orgDay":"23", "orgHour":"9", "orgMinute":"34", "countdown":"35", "platform":"", "delay":"0", "isRealTime":true, "isDateOverflow":false, "lineInfo":{ "lineInfo":{ "messages":[ ], "text":"", "priority":0 }, "lineBlocking":{ "messages":[ ], "text":"", "priority":0 } }, "fullTime":"1682235240", "orgFullTime":"1682235240", "supplement":"", "key":"43" } ], "departureInfo":{ "stopInfo":{ "text":"" }, "stopBlocking":{ "text":"" }, "areaInfo":{ "text":"" }, "areaBlocking":{ "text":"" } }, "globalInfo":"", "stationName":"Bielefeld, Ziegelstra\u00dfe", "marquee":-1, "stationInfo":[ ], "currentTime":1682233123, "currentTimeReal":1682233123 }
Q&D Example
"Bielefeld existiert, und PHP ist OK" ;)
<?php $url = "https://haltestellenmonitor.vrr.de/backend/api/stations/table"; $data = "table%5Bdeparture%5D%5BstationId%5D=23005614&table%5Bdeparture%5D%5BstationName%5D=Bielefeld+Ziegelstra%C3%9Fe&table%5Bdeparture%5D%5BplatformVisibility%5D=1&table%5Bdeparture%5D%5Btransport%5D=0%2C1%2C2%2C3%2C4%2C15%2C6&table%5Bdeparture%5D%5BuseAllLines%5D=1&table%5Bdeparture%5D%5BlinesFilter%5D=&table%5Bdeparture%5D%5BoptimizedForStation%5D=0&table%5Bdeparture%5D%5BrowCount%5D=6&table%5Bdeparture%5D%5BrefreshInterval%5D=60&table%5Bdeparture%5D%5Bdistance%5D=0&table%5Bdeparture%5D%5Bmarquee%5D=-1&table%5BsortBy%5D=0"; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); $resp = curl_exec($curl); curl_close($curl); $data = json_decode($resp, true); foreach ($data["departureData"] as $dep) { $dir = $dep["direction"]; $time = strftime("%R", $dep["orgFullTime"]); if ($dep["delay"] > 0) { $time .= " +".$dep["delay"]; } $left = ($dep["fullTime"] - time())/60; printf("+%-3d %-15s %s\n", $left, $time, $dir); }
Nextride in der Sudbrackstraße
Der nextride-Teil bestand aus einem Haskell-Programm, welches cronjob-gesteuert die Bahn-Seite für die nächstgelegene Straßenbahnhaltestelle abgriff und die Abfahrtszeiten als JSON in eine Datei schrieb. Diese Datei wurde bei Änderung vom Infobeamer (Armageddon_Notifier) per Lua-Script eingelesen und dargestellt.
Diese Lösung funktioniert aus verschiedenen Gründen leider nicht mehr, unter anderem weil das damals genutzte API der Bahn keine Daten anderer Verkehrsbetriebe mehr zur Verfügung stellt.