Transport NSW / Sydney Bus, Ferry and Train schedule

You can have as many routes as you like as long as the sensor gets a new name.

I never had to use trains, but thanks to @poebae for showing those examples.

just wondering which route the central coast/newcastle line is…

I set up a second set of sensors consuming the information from the Transport NSW one. In those, I do all kind of formatting and validations for the specific situations I need the sensor for.

Thanks. That’s just way too hard…

Hi David @poebae gave me a hand regarding the route for train lines. For the T1 line the format is: T1 North Shore, Northern & Western Line. I did some brief testing and it seemed to work.

ah. Stupid me thought they would have a shortcode like T1 in your example or CCN in mine…

1 Like

I’m getting N/A for the trains.

Capture

in config:

# Public Transport NSW
  - platform: transport_nsw
    name: 'Kariong-Gosford'
    stopid: '2250275'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Gosford-Kariong'
    stopid: '225042'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Gosford-Central'
    stopid: 'Gosford Station'
#    route: 'CCN Central Coast & Newcastle Line'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Central-Gosford'
    stopid: 'Central Station'
    route: 'CCN Central Coast & Newcastle Line'
    apikey: !secret trans_nsw

I’ve just commented out the route to see if I can ‘crack-the-code’

Edit to add: didn’t help. I also just tried Gosford and Central instead of appending Station to the end…

I can’t crack it to work from Central to Gosford no matter what I try. Always unavailable unless I blank out route.

I’ve tried just CCN as well as just Central Coast & Newcastle Line and also Central and Central Station and all permutations of both of those. No joy.

You can dig into the data on the Trip Planner API page to find specific stops and lines.

  1. Click on /departure_mon
  2. Enter the stop ID for name_dm (this page indicates that Gosford Station is 10101139). You might want to find the stop ID of the specific platform of the station.
  3. Enter today’s date for itdDate
  4. Scroll down and click the grey button ‘Try it out!’

I haven’t had a thorough look through the output but it looks like the route ID you’re looking for might be ‘Intercity Trains Net Central Coast & Newcastle Line’.

For the stops you need to enter stop IDs rather than names. Central station is 10101100.

2 Likes

Thanks! That fixed it. I used those ID’s. I had the line right and didn’t see the numerical ID’s.

In case it assists someone else:

# Public Transport NSW
  - platform: transport_nsw
    name: 'Kariong-Gosford'
    stopid: '2250275'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Gosford-Kariong'
    stopid: '225042'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Gosford-Central'
    stopid: '10101139'
    route: 'Central Coast & Newcastle Line'
    apikey: !secret trans_nsw
  - platform: transport_nsw
    name: 'Central-Gosford'
    stopid: '10101100'
    route: 'Central Coast & Newcastle Line'
    apikey: !secret trans_nsw
  - platform: template
    sensors:
      busmonitor1:
        friendly_name: "Bus Kariong to Gosford"
        value_template: >-
          {% if is_state_attr('sensor.karionggosford', 'due_in', 'n/a') %}
            No schedule found
          {% else %}
            {{ states.sensor.karionggosford.attributes.route }} in {{ states.sensor.karionggosford.attributes.due }}m ({{ states.sensor.karionggosford.attributes.delay }}m delay)
          {% endif %}
  - platform: template
    sensors:
      busmonitor2:
        friendly_name: "Bus Gosford to Kariong"
        value_template: >-
          {% if is_state_attr('sensor.gosfordkariong', 'due_in', 'n/a') %}
            No schedule found
          {% else %}
            {{ states.sensor.gosfordkariong.attributes.route }} in {{ states.sensor.gosfordkariong.attributes.due }}m ({{ states.sensor.gosfordkariong.attributes.delay }}m delay)
          {% endif %}
  - platform: template
    sensors:
      trainmonitor1:
        friendly_name: "Train Gosford to Central"
        value_template: >-
          {% if is_state_attr('sensor.gosfordcentral', 'due_in', 'n/a') %}
            No schedule found
          {% else %}
            {{ states.sensor.gosfordcentral.attributes.route }} in {{ states.sensor.gosfordcentral.attributes.due }}m ({{ states.sensor.gosfordcentral.attributes.delay }}m delay)
          {% endif %}
  - platform: template
    sensors:
      trainmonitor2:
        friendly_name: "Train Central to Gosford"
        value_template: >-
          {% if is_state_attr('sensor.centralgosford', 'due_in', 'n/a') %}
            No schedule found
          {% else %}
            {{ states.sensor.centralgosford.attributes.route }} in {{ states.sensor.centralgosford.attributes.due }}m ({{ states.sensor.centralgosford.attributes.delay }}m delay)
          {% endif %}

Glad to help :slight_smile:

I’m just happy to have an Aus-specific component for Home Assistant. Here’s to many more!

1 Like

It’s still coming up with blanks now for the Central-Gosford sensor. Sigh.

and… now working again… I can’t figure it out.

It seems to go n/a as soon as a train has left instead of going to the next one straight away whereas the gos-central just ticks over. Maybe it’s doing it when HA starts for the first train and then doesn’t tick over.

Seems to me something is missing anyway. The station is right (Gosford) but how does it know which way the train is going? I don’t see a route number to identify that direction…

That’s why I mentioned specifying the platform. If you look in the stops.txt data there are separate stop IDs for different platforms at a station. That seems to do the trick for me.

Check the API output I linked above to grab the values: https://pastebin.com/HncuFib4

There’s multiple platforms that can be used. The main issue is Gosford - I could be going North or South from there and it’s not necessarily a specific platform either. And it’s the same line/route.

I live near a station on the northern line and used this API to make a departure board a couple of months ago. I would have used this but I rolled my own without a component (you can use my example if you’re not too confident with custom components or don’t want to specify a route).

I used a few rest sensor to get departure times for all departures from my station:

- platform: rest
  resource: https://api.transport.nsw.gov.au/v1/tp/departure_mon?outputFormat=rapidJSON&coordOutputFormat=EPSG%3A4326&mode=direct&type_dm=stop&name_dm=10101200&departureMonitorMacro=true&TfNSWDM=true&version=10.2.1.42
  name: "nexttraina"    
  headers:
    Accept: application/json
    Authorization: apikey InsertAPIKeyHere
  value_template: '{{value_json.stopEvents.x.departureTimeEstimated}}'   

Where x = 0, 1, 2, 3, 4, 5

Then I made a few more sensors to get the platform ID:

  - platform: rest
    resource: https://api.transport.nsw.gov.au/v1/tp/departure_mon?outputFormat=rapidJSON&coordOutputFormat=EPSG%3A4326&mode=direct&type_dm=stop&name_dm=10101200&departureMonitorMacro=true&TfNSWDM=true&version=10.2.1.42
    name: "wheretraina"    
    headers:
      Accept: application/json
      Authorization: apikey InsertAPIKeyHere 
    value_template: '{{value_json.stopEvents.x.location.id}}' 

Where x = 0, 1, 2, 3, 4, 5

Then I made a very ugly looking automation for each of those trains:

  - alias: Build T1 message
    hide_entity: False
    trigger:
      platform: time
      seconds: '/20'
    action:
      - service: mqtt.publish
        data:
          topic: "/trains/1"
          payload_template: "{% if is_state('sensor.wheretraina', '2114171') %}(To City){% elif is_state('sensor.wheretraina', '2114172') %}(To City){% elif is_state('sensor.wheretraina', '2114173') %}(To Epping){% elif is_state('sensor.wheretraina', '2114174') %}(To Epping){% endif %} {% if (((as_timestamp(states.sensor.nexttraina.state) - as_timestamp(utcnow()) )  | float * 0.0167) | round(0) ) | float <= 0.0 %}Just left{% else %}{{ ((as_timestamp(states.sensor.nexttraina.state) - as_timestamp(utcnow()) )  | float * 0.0167) | round(0) }} min {% endif %}"

I know it looks confusing, but it’s just checking which platform the next train is, giving it a ‘friendly’ name and then tacking on the minutes to departure. The <0 is because trains get ‘sticky’ in my experience and sometimes go into 3-5 minutes of negative minutes until departure until the StopEvents list is updated, so I just replace it with a “Just Left”.

The result is my departure board:

Capture

I can see the platform from my computer so I can confirm that the timing is spot on. When I’m at the station and comparing it to the official departure boards it’s within 10 seconds. I then use google home to activate a script and it reads out my next train departures to me.

Does anybody have an idea on how I could split it up to only show departures, say, to the city? I can’t split by platform as the trains leave from both, but mostly one of them. I’m not really advanced enough to know about arrays and iterating through JSON values until a match is found (i.e. only display StopEvents 1, 3, 5 and 6 based on a platform match).

2 Likes

Great component @DownUnder, I just found it when trawling the forum. I set it up last night and is working great.

The stop I am at has multiple lines that service the city, L70, 270, 271, 273, 274. Is there anyway to group all these into one sensor or front end to only show the next bus overall rather than having to add multiple sensors?

Oh, and one more which I think may be a little much :slight_smile: Can you add the bus “fullness” for want of a better term. On Tripview and the websites it shows how many seats are filled (icon for one, two or three seats depending on how full the bus is)

Many thanks for your great work!

Cheers

See my post above. You can do it without this component.

If the GTFS data has the fullness (which I’m pretty sure most do), you can extract it from the JSON data.

1 Like

I’ve been configuring up this new component, and i have hit a bit of a snag, which maybe only applicable in my particular circumstance.

I note there are 4 configurable values.

stop_id & route (which are relevant in this case) and api_key and name

The problem I face is with the way the ferries work here on Sydney Harbour. The route always remains the same and the stop id is always the same regardless of which direction the ferry is heading.

So i can’t just display ferries heading to circular quay, and i won’t know which direction the ferry is heading.

If i put in a stop_id: 10102008 for Balmain Wharf

This all works fine, and lets me know how many minutes prior to the ferry arriving.
Unfortunately, unlike the train platforms that have stop_id: that only travels in one direction, the stop_id: 10102008 travels in 2 different directions. For example, towards the city and away from the city.
So i don’t seem to have a way to define or filter which direction the countdown timer is referring to.

if i define a route as ‘F8 Cockatoo Island’ thats fine, but that covers ferries heading in both directions

so i went to the nsw transport api site
https://opendata.transport.nsw.gov.au/node/601/exploreapi#!/default/tfnsw_dm_request

and put the stop_id: 10102008 in, and the returns do actually give me a little more data than i am able to define in my sensor

So some values i have that i can see that are unique, but not configurable are as follows
for example
destination": “name”: “Circular Quay”,
or
description “Circular quay to Cockatoo Island”

seem to be unique and would indicate the direction of travel, even though the stop id and route are the same for both returns

Perhaps a solution could lie in having a configurable value of “destination” that could be integrated into the sensor for transport_nsw ?

Or could someone think of another way to achieve what i’m after?

transport api response paste as below against stop_id: 10102008 showing 2 trips, returning same stop_id and same route, but heading in different directions

{
“version”: “10.2.1.42”,
“systemMessages”: [],
“locations”: [
{
“id”: “10102008”,
“name”: “Balmain Wharf, Balmain”,
“disassembledName”: “Balmain Wharf”,
“coord”: [
-33.85475,
151.18611
],
“type”: “stop”,
“matchQuality”: 100000,
“isBest”: false,
“parent”: {
“id”: “95333002|1”,
“name”: “Balmain”,
“type”: “locality”
},
“assignedStops”: [
{
“id”: “10102008”,
“name”: “Balmain Wharf”,
“type”: “stop”,
“coord”: [
-33.85475,
151.18611
],
“parent”: {
“name”: “Balmain”,
“type”: “locality”
},
“modes”: [
9
],
“connectingMode”: 100
}
]
}
],
“stopEvents”: [
{
“location”: {
“id”: “20413”,
“isGlobalId”: true,
“name”: “Balmain, Balmain Wharf”,
“type”: “platform”,
“coord”: [
-33.85456,
151.18627
],
“parent”: {
“id”: “10102008”,
“name”: “Balmain, Balmain Wharf”,
“disassembledName”: “Balmain Wharf”,
“type”: “stop”,
“parent”: {
“id”: “95333002|1”,
“name”: “Balmain”,
“type”: “locality”
}
}
},
“departureTimePlanned”: “2018-10-27T01:36:00Z”,
“transportation”: {
“id”: “nsw:090F8: :R:sj2”,
“name”: “Sydney Ferries Netwo F8 Cockatoo Island”,
“disassembledName”: “F8”,
“number”: “F8 Cockatoo Island”,
“iconId”: 10,
“description”: “Cockatoo Island to Circular Quay”,
“product”: {
“class”: 9,
“name”: “Sydney Ferries Netwo”,
“iconId”: 9
},
“operator”: {
“id”: “112”,
“name”: “Sydney Ferries”
},
“destination”: {
“name”: “Circular Quay”,
“type”: “stop”
},
“properties”: {
“tripCode”: 57,
“mtSubcode”: “0”
},
“origin”: {
“name”: “Cockatoo Island Wharf”,
“type”: “stop”
}
},
“hints”: [
{
“content”: “At low tide ramp gradients increase. Wheelchair assistance may be required.”
}
],
“properties”: {
“WheelchairAccess”: “true”,
“RealtimeTripId”: “nsw-9-F8- -sj2-3-16-AB53”,
“PlanLowFloorVehicle”: “1”,
“PlanWheelChairAccess”: “1”
}
},
{
“location”: {
“id”: “20413”,
“isGlobalId”: true,
“name”: “Balmain, Balmain Wharf”,
“type”: “platform”,
“coord”: [
-33.85456,
151.18627
],
“parent”: {
“id”: “10102008”,
“name”: “Balmain, Balmain Wharf”,
“disassembledName”: “Balmain Wharf”,
“type”: “stop”,
“parent”: {
“id”: “95333002|1”,
“name”: “Balmain”,
“type”: “locality”
}
}
},
“departureTimePlanned”: “2018-10-27T01:57:00Z”,
“transportation”: {
“id”: “nsw:090F8: :H:sj2”,
“name”: “Sydney Ferries Netwo F8 Cockatoo Island”,
“disassembledName”: “F8”,
“number”: “F8 Cockatoo Island”,
“iconId”: 10,
“description”: “Circular Quay to Cockatoo Island”,
“product”: {
“class”: 9,
“name”: “Sydney Ferries Netwo”,
“iconId”: 9
},
“operator”: {
“id”: “112”,
“name”: “Sydney Ferries”
},
“destination”: {
“name”: “Cockatoo Island”,
“type”: “stop”
},
“properties”: {
“tripCode”: 10,
“mtSubcode”: “0”
},
“origin”: {
“name”: “Circular Quay”,
“type”: “stop”
}
},
“hints”: [
{
“content”: “At low tide ramp gradients increase. Wheelchair assistance may be required.”
}
],
“properties”: {
“WheelchairAccess”: “true”,
“RealtimeTripId”: “nsw-9-F8-
-sj2-3-10-AA51”,
“PlanLowFloorVehicle”: “1”,
“PlanWheelChairAccess”: “1”
}
},

Hi Peter,

I will test it and might have to fix the sensor. Will let you know once i have done some testing.

Cheers

Works a treat now with ‘Destination’ Home assistant V83.1

transport_nsw

1 Like