Introduction
This tutorial shows how to track public transport vehicles in real-time using GTFS in openHAB.
While the example uses a bus line, the method works for any route worldwide, provided you have route geometry (OSM or GTFS) and schedule data.
Note: My example currently use German cities, but the workflow can be adapted to any city by adjusting Overpass Turbo queries and GTFS sources.
Prerequisites
- openHAB 5.x or higher
- Node.js installed
- Node scripts:
prompt.js(for generating trip/stop JSON) please rename it after downloadlinientracker.js(for tracking and mapping) please rename it after download
- Internet access
1. Retrieve the Route
1.1 Open Overpass Turbo
Visit: https://overpass-turbo.eu
1.2 Example Query (Anonymized)
Replace ExampleCity and the line reference with your target city and route:
[out:json][timeout:25];
// Define search area
area["boundary"="administrative"]["admin_level"="4"]["name"="ExampleCity"]->.searchArea;
// Select route relation
relation["route"="bus"]["ref"="BUS_XX"](area.searchArea);
out geom;
Replace
"bus"with"tram"or"train"for other types of routes.
1.3 Export Route
- Run the query
- Click “Data” → Export JSON
- Save as:
busroute.txt
2. Generate GTFS-like JSON for a Stop
2.1 Run Node Script
node prompt.js
2.2 Follow Prompts
- Choose the stop you want to track
- Select your trip remember stop sequence and trip id
- Confirm parameters
2.3 Save Output
- Save the resulting JSON, e.g.:
trip.json - Note the Trip ID (e.g.,
TRIP_XX) for tracking
3. Track the Route with linientracker.js
3.1 CLI Example (can be called in any rule)
node linientracker.js \
--route busroute.txt \
--trip trip.json.txt \
--stop-sequence 5 \
--silent \
--trip-id TRIP_XX \
--output-json output.json
Parameter Explanation
| Parameter | Purpose |
|---|---|
--route |
Route JSON exported from Overpass Turbo |
--trip |
Trip JSON generated from prompt_newnew.js |
--stop-sequence |
Stop sequence number in the trip |
--silent |
Suppress CLI output |
--trip-id |
Trip ID to track for delays |
--output-json |
JSON output with current position, mapped stops, arrival info |
--help |
for more parameters with descripton |
3.2 JSON Example
{
"trip": {
"id": "TRIP_XX",
"has_realtime_data": false,
"status": "not_started"
},
"current_position": {
"timestamp": "2025-XX-XXTXX:XX:XX.XXXZ",
"coordinates": {
"latitude": 0.000000,
"longitude": 0.000000,
"corrected": false,
"correction_distance_m": 0
}
},
"current_segment": null,
"query_stop_info": {
"sequence": 5,
"name": "Example Stop",
"arrival": {
"scheduled": "2025-XX-XXTXX:XX:00.000Z",
"estimated": "2025-XX-XXTXX:XX:00.000Z",
"delay_seconds": 0,
"has_realtime_delay": false
},
"time_until_arrival_seconds": 1800,
"status": "before_start"
},
"upcoming_stops": [
{"sequence": 0,"name": "Start Stop","arrival":{"scheduled":"2025-XX-XXTXX:XX:00.000Z","delay_seconds":0,"has_realtime_delay":false}},
{"sequence": 1,"name": "Intermediate Stop A","arrival":{"scheduled":"2025-XX-XXTXX:XX:00.000Z","delay_seconds":0,"has_realtime_delay":false}},
{"sequence": 2,"name": "Intermediate Stop B","arrival":{"scheduled":"2025-XX-XXTXX:XX:00.000Z","delay_seconds":0,"has_realtime_delay":false}}
],
"metadata": {
"generated_at": "2025-XX-XXTXX:XX:XX.XXXZ",
"schedule_stop_count": 00
}
}
4. Notes on Worldwide Use
- Supported countries: Technically any — only the Overpass Turbo query and GTFS source need to be adjusted.
- Main difficulty: Obtaining GTFS data. Options:
- Official transport authority feeds (
gtfs.zip) - OpenStreetMap relations converted into GTFS-like JSON
- Manual schedule entry if no feed exists
- Official transport authority feeds (
- Once the route and trip JSON are generated, tracking works identically worldwide.
linientracker.js.pdf (106.0 KB)
prompt.js.pdf (8.4 KB)