If, like me, you use the rail network to get to work you’ll understand how annoying it is when I turn up to the station and see a big group of people and no trains.
To get around this I use the UK Transport API, through Node Red and MQTT, to make a dashboard in Home Assistant and notify my phone if there are more than 3 delayed trains and the delays are more than 5 minutes on average. (Don’t worry this tolerance is configurable later on).
This guide will help you, through the installation of a few Home Assistant Add-ons and minimal editing hopefully achieve this high state of zen.
- You use UK rail (or bus!) network to get to work
- Home Assistant, Node Red and MQTT working together.
- A geolocating device tracker and zones set up for the stations, or you’ll get notifications about trains to work when you’re already there!
- An existing Notification service. I use telegram as it has actionable notifications. You may need to tailor some nodes discussed later for it to work for you.
The first thing we’ll need to do is set up an API account at transport API. To do so go to their developer site and Sign up. We’ll need to keep the
App ID and
Key for requests later on.
In this guide I’ll be using my commute, from
London Waterloo and back. These stations are used in the API and in our supporting nodes and home assistant configuration under their
CRS (Computer Reservation System) codes
WAT respectively. Please replace them using your station codes. They can be found in the railway codes website.
[In the API request] Stations can be referenced in the form
WATfor Waterloo station.
WATRLOOfor Waterloo station. if no scheme is specified,
CRSis assumed by default, so
watimplicitly references the
WATfor Waterloo station.
Home Assistant Setup
We’ll need to set up some items within Home assistant to track the state of what’s happening and for display in the lovelace interface.
Input Booleans for Notification Suppression
First we set up some input booleans to allow us to suppress notifying us through actionable notifications.
Input Booleans for Zone entry
We’ll also add some input booleans that we’ll toggle through a
device_tracker when we arrive at stations to suppress notifications when we’re already on a train.
MQTT sensors for saving state
We’ll also need to set up a couple of mqtt sensors, which take the state as the number of late trains and a whole lot of information as attributes. Mine uses the CRS codes again for the station as part of the name.
Now that we have these booleans and sensors setup We can start polling the API for data. Note that the Transport API has a 1000 request limit per day. This equates to one call every 87 seconds. In this case we have four requests. Our interval will need to be a minimum of 348 seconds. I’ve rounded that up to 6 minutes in the below flow.
Note that unless we have 24⁄7 trains and a 24⁄7 need for updates we can limit the window it makes requests in the
timestamp node and therefore increase frequency! See timestamp injection below.
The full flow is here. Let us break it up into sections to see what it does.
Part 1. Getting the data from the API
The top left node is where we start the flow activity. Earlier I noted that we can run the flow every 6 minutes, but if we know we’re not going to need train information early in the morning, late at night or during weekends we can amend this node’s details to turn off days and limit the hours the API is polled. This allows us to increase frequency.
In the next nodes we set the train specific payload, each of these items get used in the API request. We ask for arrivals and departures because it can easily be the case that problems occur further along the train’s journey and the departures may not be experiencing them yet.
Here’s an arrival node:
Next we set up our API ID and Key from Transport API. We look for arrival and departures slightly differently.
Note we’ll need to add the details into a node in Part 2, where the API is polled again, I’m sure there is another way to set this more globally? - answers in the comments!
Handling Returned Data
After the HTTP request itself, we handle the data, squishing it into what we need as state and attributes. This node is full of information and the whole JS is here:
Let us look a bit more about what happened there:
* We get the list of trains and reverse it (to help remove overtaken trains as we go backwards on arrival time. If the arrival time is later than the train after it we remove it…)
* For each train we use
time_check to work out which trains to remove, and we work out the delay and duration, added them to the train object.
* After removing overtaken trains we work out the number of delayed trains (we’ll use that as the state), the average delay and the maximum delay
* Just before ending we’ll duplicate the attributes into a payload item called telegram, this way the payload key is predictable with different trains before sending that down to Part 2.
* Finally we work out which MQTT topics to post to and send it to MQTT
Part 2. Delay notifications
fromattribute * We check to see if notifications for this line are suppressed (see part 3) * We check to see if we’ve already passed through the station (see part 4) * We then push the data back into the payload ready for the meat of the notification in the following gist: - The main things to worry about here is line 5 where we set the tolerance to ignore less than 3 late trains and line 11 the tolerance for how delayed they must be before a notification. - Lots of this node is some lovely work into moving things into different variables and adding ’s’ to plurals. - However, there is a second line in the notification that says
5 Cancelledetc. We look through the attributes for a whitelist of problem statuses and their sums for that. - Finally we build the payload string, the message itself and set up the actionable notifications for part 3
Part 3. Suppressing unwanted notifications
The first one triggers as if we entered a station, we’ll look at that in Part 4.
The other two callbacks turn on the suppression boolean. For the
30 it then sets a timer to turn it off again. For
DAY we rely on Part 5 where an overnight rigger turns them off.
Part 4. Location-based notifications
`Platform 2 08:32 to London Waterloo` `Platform 2 08:32 to London Waterloo (exp 08:47)`
Part 5. Resetting overnight.
Then add this view:
You’ll notice there are tolerances throughout the dual gauges, for some reason lovelace copied them around and I’m scared to remove them, it means there’s a lot of places to update if you want to change tolerances., sorry!
TLDR; Things we’ll need to change
So, in summary, this is all of the detail we’ll need to change, I think, to get this working: * Start and end stations * Any references to sensors and booleans we’ve amended * Telegram Chat ID (or the payload setup/callback for other notification services) * Transport API ID and Key * Tolerances to silence alarms * Device Tracker name * Zone Names from the device trigger.
You’ll also see at the stat of my part 2 I have extra booleans in place to test if the following are true and bailing out from sending delay notifications (but still store it as state in Home Assistant) * if its a workday * if I’ve not set myself as driving to work * if I’ve set myself as Working From Home * if I’m still asleep
This all goes fine until the data isn’t correct. During the write up of this guide I turned up and there were no trains but the train company didn’t update the service status and the TransportApi was happily telling me everything was fine because that’s what they were being told. Its a rarity though.