I’ve integrated the data from local public transport (Germany, Dresden region, VVO) to my HABPanel. They provide a nice API that gives tram, bus and train data with real-time delay information. My solution is a small HTML/Javascript file that is shown in an iFrame widget.
Here is a screenshot (orangetree style) and the code.
Edit 3. Jan. 2018: update script for missing real time data, debug formatting
<!DOCTYPE html>
<html>
<body>
<span id="zug" style="font-size:24px; color:#ff9c27; font-family: Helvetica, sans-serif;"></span>
<script>
// find the station ID with the Pointfinder API (replace Keyword by part of station/stop name *rly*!)
// https://webapi.vvo-online.de/tr/pointfinder?format=json&query=Keyword
const stationID = 12345678; //
const statusTable = {
"Delayed" : "verspätet",
"InTime" : "pünktlich",
// the "Canceled" is never returned, but a " fällt aus" is added to the direction, historical reasons *sic*
"Canceled": "Zug fällt aus !",
"undefined" : "unbekannt"
};
var limit = 3;
var text = "";
document.getElementById("zug").innerHTML = '<span style="font-size:22pt; color:#ff9c27; float:left"> Lade Daten vom VVO ... </span>';
// get JSON data from the VVO api
xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET", "https://webapi.vvo-online.de/dm?format=json&mot=SuburbanRailway&stopid="+stationID, false);
xmlhttp.send();
// parse the JSON to a variable
var obj = JSON.parse(xmlhttp.responseText);
document.getElementById("zug").innerHTML = '<span style="font-size:22pt; color:#ff9c27; float:left"> Got it ... </span>';
// Daten für Züge
for (i=0; i < limit; i++) {
var linedir = '<b>'+obj.Departures[i].LineName + " : " + obj.Departures[i].Direction+' </b>';
var direction = obj.Departures[i].Direction;
if (i > 0) { text += '<br><hr style="color:#272727;">' };
var schedule = parseInt(obj.Departures[i].ScheduledTime.substring(6, 19))
var time = new Date(schedule);
var soll = time.toTimeString().substring(0, 5);
var status = statusTable[ obj.Departures[i].State ] ;
document.getElementById("zug").innerHTML = '<span style="font-size:22pt; color:#ff9c27; float:left"> x'+status+'x </span>';
if (status == "unbekannt") {
document.getElementById("zug").innerHTML = '<span style="font-size:22pt; color:#ff9c27; float:left"> hier </span>';
delay = '';
status = 'keine Echtzeitdaten verfügbar.';
color = 'grey';
} else {
var status = statusTable[ obj.Departures[i].State ] ;
var delay = Math.floor(((parseInt(obj.Departures[i].RealTime.substring(6, 19))) - schedule)/60000);
if (delay > 0) {
delay = '<b style="color:red"> +'+delay+'</b>';
color = 'red';
} else if (direction.substring(direction.length-10, direction.length) == " fällt aus") {
linedir = '<b>'+obj.Departures[i].LineName + " : " + obj.Departures[i].Direction.substring(0, obj.Departures[i].Direction.length-10)+' </b>' ;
delay = '';
status = "Zug fällt aus !";
color = 'red';
} else {
delay = '';
color = 'green';
};
};
//delay = delay +" L"+ obj.Departures.length+" i"+i+" n"+n;
//var status = obj.Departures[i].State + " = "+ statusTable[ obj.Departures[i].State ] ;
//status => statusTable[status] || status;
text += linedir + '<span style="float:right">' + soll + delay + '</span><br><i style="color:'+color+'">'+ status + '</i>';
document.getElementById("zug").innerHTML = '<span style="font-size:22pt; color:#ff9c27; float:left"> Zug '+i+' </span>';
};
//text += '<br><span style="font-size:10pt; color:#222222; float:left"> Danke VVO </span>';
document.getElementById("zug").innerHTML = text;
</script>
</body>
</html>