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.
Suche von Stationen
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"
Feldwerte
- table[departure][transport]
| 0 | ICE/IC/EC |
| 1 | Zug |
| 2 | S-Bahn |
| 3 | U-Bahn |
| 4 | Straßenbahn |
| 5 | Bus |
| 6 | Schwebebahn |
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.