Manchester Metrolink REST API to Sensor

I’m looking to be able to integrate data from Manchester’s Metrolink REST API into Home Assistant. Looking at the REST documentation though, I have 2 stumbling blocks:

  1. The endpoint I am querying has a space in the URI, so do I need to wrap it in quotes or will it work with the spaces left in?
GET https://api.tfgm.com/odata/Metrolinks?$filter=StationLocation eq 'Chorlton'
  1. The JSON I get back from the API is not hierarchical, so is there a way for me to group the results I get back into a set of sensors and attributes. As an example I’d like to produce something like this:

Sensor 1: sensor.metrolink_outgoing
Attributes: dest0, status0, wait0, dest1, status2, wait1, etc…

Sensor 2: sensor.metrolink_incoming
Attributes: dest0, status0, wait0, dest1, status2, wait1, etc…

From this JSON:

{
  "@odata.context": "https://opendataclientapi.azurewebsites.net/odata/$metadata#Metrolinks",
  "value": [{
    "Id": 897,
    "Line": "South Manchester",
    "TLAREF": "CHO",
    "PIDREF": "CHO-TPID01",
    "StationLocation": "Chorlton",
    "AtcoCode": "9400ZZMACHO1",
    "Direction": "Outgoing",
    "Dest0": "East Didsbury",
    "Carriages0": "Double",
    "Status0": "Departing",
    "Wait0": "0",
    "Dest1": "Manchester Airport",
    "Carriages1": "Single",
    "Status1": "Due",
    "Wait1": "9",
    "Dest2": "Manchester Airport",
    "Carriages2": "Single",
    "Status2": "Due",
    "Wait2": "19",
    "Dest3": "",
    "Carriages3": "",
    "Status3": "",
    "MessageBoard": "ENGINEERING WORKS - Services are operating East Didsbury - Deansgate Castlefield ONLY. Replacement Buses are in operation between Deansgate and Central Park. Visit www.tfgm.com for info",
    "Wait3": "",
    "LastUpdated": "2021-08-04T17:10:21Z"
  }, {
    "Id": 898,
    "Line": "South Manchester",
    "TLAREF": "CHO",
    "PIDREF": "CHO-TPID02",
    "StationLocation": "Chorlton",
    "AtcoCode": "9400ZZMACHO2",
    "Direction": "Incoming",
    "Dest0": "Cornbrook",
    "Carriages0": "Single",
    "Status0": "Due",
    "Wait0": "1",
    "Dest1": "Deansgate Castlefield",
    "Carriages1": "Double",
    "Status1": "Due",
    "Wait1": "5",
    "Dest2": "Cornbrook",
    "Carriages2": "Single",
    "Status2": "Due",
    "Wait2": "12",
    "Dest3": "",
    "Carriages3": "",
    "Status3": "",
    "MessageBoard": "ENGINEERING WORKS - Services are operating East Didsbury - Deansgate Castlefield ONLY. Replacement Buses are in operation between Deansgate and Central Park. Visit www.tfgm.com for info",
    "Wait3": "",
    "LastUpdated": "2021-08-04T17:10:21Z"
  }]
}

Is this even possible given the flat layout of the JSON I get from the API?

The data structure isn’t flat it’s a list. Try this tool, https://jsonpathfinder.com/

You should end up with something like ["value"][0]... and ["value"][1]...

1 Like

For 1), you have tu urlencode the url, e.g.

https://api.tfgm.com/odata/Metrolinks?$ilter=StationLocation%20eq%20%27Chorlton%27
1 Like

Many thanks both!

I now have a working sensor for incoming and outgoing trams. In case anyone else is searching in the future for TFGM Metrolink sensors, here’s my config. You’ll just need to adjust the StationLocation in the resource for the station you are interested in pulling data for:

rest:
    scan_interval: 60
    resource: "https://api.tfgm.com/odata/Metrolinks?$filter=StationLocation eq 'Chorlton'"
    sensor:
      - name: metrolink_outgoing
        json_attributes_path: "$.value[0]"
        value_template: "OK"
        json_attributes:
          - "Dest0"
          - "Status0"
          - "Wait0"
          - "Dest1"
          - "Status1"
          - "Wait1"
          - "Dest2"
          - "Status2"
          - "Wait2"
          - "Dest3"
          - "Status3"
          - "Wait3"
          - "MessageBoard"
          - "LastUpdated"
      - name: metrolink_incoming
        json_attributes_path: "$.value[1]"
        value_template: "OK"
        json_attributes:
          - "Dest0"
          - "Status0"
          - "Wait0"
          - "Dest1"
          - "Status1"
          - "Wait1"
          - "Dest2"
          - "Status2"
          - "Wait2"
          - "Dest3"
          - "Status3"
          - "Wait3"
          - "MessageBoard"
          - "LastUpdated"
    headers:
      Ocp-Apim-Subscription-Key: !secret tfgm_token

secrets.yaml line should look something like the below (but obviously replacing the XXXXXX with your API access key:

tfgm_token: XXXXXXXXXXXX

4 Likes

Bonus of a lovelace Markdown card to show the data:

type: markdown
content: >-
  <strong>Next trams</strong><br>{{
  state_attr("sensor.metrolink_incoming","Dest0") }} due in {{
  state_attr("sensor.metrolink_incoming","Wait0") }} minutes <br>{{
  state_attr("sensor.metrolink_incoming","Dest1") }} due in {{
  state_attr("sensor.metrolink_incoming","Wait1") }} minutes <br>{{
  state_attr("sensor.metrolink_incoming","Dest2") }} due in {{
  state_attr("sensor.metrolink_incoming","Wait2") }} minutes <br><br>{{
  state_attr("sensor.metrolink_incoming","MessageBoard") }}
title: Metrolink Chorlton (Towards Town)

6 Likes

Well this is mighty handy, thanks!

Out of interest, if I wanted to do this with more than one tram stop, then what would the code need to look like in the configuration.yaml file?

Thanks for your help.

No problem, glad to hear it’s useful to others!

As for adding additional stations, it should be a case of just modifying the filter in the API call to include additional stations and then amending the sensors and markdown accordingly. Not something I have tried, but seems to be achievable on the face of it.

Hi, popped into my head that this would be useful and found this post.

Brilliant as I’ve not got the skills then TFGM have apparently stopped issuing keys. Joy.

Ah well will keep an eye on it. Hopefully they’ll open it up again at some point and I can take advantage of your hard work.

Cheers