Real-Time Public Transport Tracking in openHAB Using GTFS

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 download
    • linientracker.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

  1. Run the query
  2. Click “Data” → Export JSON
  3. 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
  • Once the route and trip JSON are generated, tracking works identically worldwide.
    linientracker.js.pdf (106.0 KB)
    prompt.js.pdf (8.4 KB)