Two tools for National Rail, both sending results to Discord:
- Disruption Monitor — runs every 30 minutes via GitHub Actions and alerts when there is a disruption or cancellation on your commute route
- Departure Checker — triggered by Home Assistant when you arrive at a station, showing the next 3 trains to your destination with platform and arrival time
- Python 3
pip install -r requirements.txt(requests)- A Discord webhook URL
pip install -r requirements-dev.txt
pytest tests/ -vTests run automatically on every push and pull request via the Tests GitHub Actions workflow. main is protected — changes need to land via a pull request with a passing Tests check, direct pushes are rejected.
Two checks run on every execution:
- RTT API — detects formally cancelled or delayed (≥5 min) services on the configured routes
- National Rail status page — detects active unplanned incidents for the configured operators, filtered to only those relevant to your route. On each run, the calling points of the next few live services are fetched from RTT and unioned to build the station list dynamically, ensuring all intermediate stops are captured regardless of whether the first service is fast or stopping. An NR alert is included if it either mentions a station on your route, or contains no specific station reference (e.g. a general hot weather advisory). Disruptions affecting stations elsewhere on the same operator are silently skipped.
To avoid notification fatigue, results are diffed against the previous run's state. A notification is only sent when something changes — a new disruption appears, an existing one is updated, or one is cleared. Runs where nothing has changed are silent. State is persisted between runs using the GitHub Actions cache (state.json). The cache key includes the current date, giving an effective 1-day TTL — state is never carried over to the next calendar day, so each day starts fresh.
| Variable | Required | Description |
|---|---|---|
DISCORD_WEBHOOK_URL |
Yes | Your Discord webhook URL |
RTT_REFRESH_TOKEN |
Yes | JWT token from api-portal.rtt.io |
ROUTES |
Yes | Comma-separated FROM:TO CRS pairs to check for delays/cancellations (e.g. KGX:EDB,PAD:BRI) |
OPERATOR_CODES |
No | Comma-separated National Rail operator codes to watch for incidents (default: LE,SX — Greater Anglia and Stansted Express) |
DELAY_THRESHOLD_MINS |
No | Minutes late before a delay is reported (default: 5) |
- Go to Settings > Secrets and variables > Actions.
- Under Secrets, add
DISCORD_WEBHOOK_URLandRTT_REFRESH_TOKEN. - Under Variables, add
ROUTES(required for the scheduled run — there is no built-in default) and optionallyOPERATOR_CODES.
| Trigger | Behaviour |
|---|---|
| Scheduled (every 30 min) | Notifies only when something has changed since the last run (new, updated, or cleared disruption) |
Manual (workflow_dispatch) |
Always notifies — shows full current state regardless of previous run; updates the state cache |
pip install -r requirements.txt
export DISCORD_WEBHOOK_URL="your_url"
export RTT_REFRESH_TOKEN="your_jwt"
export ROUTES="KGX:EDB,PAD:BRI"
export OPERATOR_CODES="LE,SX"
python3 train_status.py*/30 * * * * DISCORD_WEBHOOK_URL="your_url" RTT_REFRESH_TOKEN="your_jwt" ROUTES="KGX:EDB,PAD:BRI" OPERATOR_CODES="LE,SX" /path/to/.venv/bin/python3 /path/to/train_status.pyWhen triggered, queries the Realtime Trains API for the next 3 departures from a given station to a given destination, and sends a Discord notification showing the departure time, platform, and arrival time for each service. Delayed trains show the original scheduled time with a strikethrough.
Sign up at api-portal.rtt.io. Once registered, create a token under your account. On the token detail page, copy the issued access token (the long JWT string beginning with eyJ) — this is your refresh token.
Go to Settings > Secrets and variables > Actions and add:
| Secret | Value |
|---|---|
RTT_REFRESH_TOKEN |
The JWT token copied from your api-portal.rtt.io token page |
DISCORD_WEBHOOK_URL is shared with the disruption monitor — add it once.
pip install -r requirements.txt
export DISCORD_WEBHOOK_URL="your_url"
export RTT_REFRESH_TOKEN="your_jwt"
export FROM_CRS="LST"
export TO_CRS="BIS"
export FROM_NAME="London Liverpool Street"
export TO_NAME="Bishops Stortford"
python3 departure_check.pyGo to Actions > Departure Check > Run workflow and fill in the station CRS codes. Use this to test before setting up Home Assistant.
Common CRS codes:
| Station | CRS |
|---|---|
| London Liverpool Street | LST |
| Bishops Stortford | BIS |
| Cambridge | CBG |
| Stansted Airport | SSD |
| Tottenham Hale | TOM |
| Stratford | SRA |
A full list is available at nationalrail.co.uk/stations.
This is the main way to trigger the departure checker automatically when you arrive at a station.
In Home Assistant, go to Settings > Areas & Zones > Zones and add a zone for each station you commute from. Set the radius to roughly 200m.
Go to GitHub > Settings > Developer settings > Personal access tokens > Fine-grained tokens and create a token scoped to this repository with Contents: Read and Write permission (required to trigger repository_dispatch).
Add your GitHub PAT to Home Assistant's secrets.yaml (never put tokens directly in configuration.yaml):
# secrets.yaml
github_pat: YOUR_GITHUB_PATThen add this to configuration.yaml:
rest_command:
train_departure_check:
url: "https://api.github.com/repos/cttech-io/TrainStatusChecker/dispatches"
method: POST
headers:
Authorization: "Bearer !secret github_pat"
Accept: "application/vnd.github.v3+json"
payload: >-
{"event_type":"departure-check","client_payload":{"from_crs":"{{ from_crs }}","to_crs":"{{ to_crs }}","from_name":"{{ from_name }}","to_name":"{{ to_name }}"}}
content_type: "application/json"Add entries like this to your automations.yaml:
- id: '1749999999999'
alias: "Trains at Liverpool Street → Bishops Stortford"
triggers:
- trigger: zone
entity_id: device_tracker.your_phone
zone: zone.london_liverpool_street
event: enter
conditions: []
actions:
- action: rest_command.train_departure_check
data:
from_crs: "LST"
to_crs: "BIS"
from_name: "London Liverpool Street"
to_name: "Bishops Stortford"
mode: singleDuplicate for each station/destination pair. The GitHub Actions job typically starts within 30–60 seconds of the trigger, so the Discord notification arrives shortly after you reach the station.
The Home Assistant companion app for both iOS and Android reports location automatically. Ensure Background App Refresh (iOS) or Background Location (Android) is enabled, and that the device tracker entity matches device_tracker.your_phone in the automation above.