Dutch Public Transport sensor (ovapi.nl)

Hi all,

I’m a beginner in python scripting but with some help I’ve created a script that gets information of a tram stop from a puplic transport Api provider in the Netherlands :slight_smile:

The example works and creates a python dictionary, in the key [‘stops’] a list of dictionaries is created for the stop information.

I’m still figuring out the way that sensors are build, I’m using this example

So it’s a work in progress, I’ll cross my fingers for a release soon!

The repro is located here: https://github.com/Paul-dH/Home-Assisant-Sensor-OvApi

1 Like

Nice work Paul. I am using it and it works fine!

Thanks! I’ve planned to work on a few small bugs in the near future (when there are no new arivals the script gives a error). Please let me know if you face any issues.

I hope you don’t mind, but I made a pull request to fix a few bugs.
I also added versioning to track new releases

1 Like

Nice! You’ve done a great job. This was still hunting me on my todolist. I’m testing your version now, so fingers crossed. I’m a bit new to Git, but I will asap approve your changes :slight_smile:

Thanks again, really appreciate it!

Hi @Pippyn,

I’ve tested your changes and there are no more errors in the logs. I did found a issue with my template sensors, they stopped working. This could be because the delay property is missing in the departures object. It looks like you’ve changed the code to return all the departures as a string :slight_smile:

I’m using the following template to show 5 rows of departure times beneath each other, are you using another or are you using the sensor in a different way than me?

  - platform: ovapi
    name: Tram_6
    stop_code: '9505'
    route_code: '32009505'
  - platform: template
        friendly_name: Vertrek Tram 6
        entity_id: sensor.tram_6__stop_9505
        icon_template: "{{ state_attr('sensor.tram_6__stop_9505','icon') }}"
        value_template: >
          {%- set i = 0 %}
          {%- set departure = state_attr('sensor.tram_6__stop_9505','departures')[i] if state_attr('sensor.tram_6__stop_9505','departures')[i] is defined else None %}
          {%- if departure %}
            {%- set message = ' - Vertraging: %s'%departure['Delay'] if departure['Delay'] | int > 0 else '' %}
            {{ departure['TargetDepartureTime'] }}{{ message }}
          {%- else %}
            Nog geen gegevens
          {%- endif %}
        copy of tram_6_departure1

This results in the screenshot below, notice the “Nog geen gegevens”?:


I created these separate sensors to build a cool picture glance card for Lovelace in the future, I have to admit though that I’m still figuring out the best way to combine this with departures of the bus.

Great! Yes the departures are now a string, because a dictionairy doesn’t work. But you can use the delay attribute for the next bus/train if you want? Or what do you want to display with these sensors? Maybe it is easier to build those sensors in as an option?

I’m on Discord tonight, perhaps we can exchange ideas?

I was able to change my template sensor for the first arrival to:

{%- set i = 0 %}
{%- set delay = state_attr('sensor.tram_6','delay') %}
{%- set departure = state_attr('sensor.tram_6','departures')[i] if state_attr('sensor.tram_6','departures')[i] is defined else None %}
{%- if departure %}
{%- set message = ' - Vertraging: %s min'%delay if delay | int > 0 else '' %}
{{ departure }}{{ message }}
{%- else %}
Nog geen gegevens
{%- endif %}

This sensor now returns the time and when there is a delay found it will add ’ - vertraging: % min’ so this works.

The dictionary did work with the departures attribute, the only attribute that can’t be a dict is the state. At least someone told me and the tests were successful.

I used it to return a dict with two properties (TargetDepartureTime and Delay). So in addition to the original sensor I created 5 template sensors (with the help of Pedro), each template sensor returns just one TargetDepartureTime and the corresponding Delay from the original dict. I did like the fact that I could see a delay in advance, for example on the third arrival.

Then I created one group to display the 5 template sensors in a card. This worked good for a while :slight_smile:

What do you mean with creating sensors as a option? Does that mean that it’s possible to create a option to generate the template sensors with the correct data using the ovapi.py script? This would provide a much more user friendly way to use the sensor, the users wouldn’t have to build the template sensors then.

Well it didn’t for me, that was the reason for me to look into your code. It displayed something like Object.object for each departure time in the sensor details. But I will have a look if I can change it back to a dict and have it display correctly.

Yeah, that’s what I meant :slight_smile:

So the configuration file could look something like:

  - platform: ovapi
    name: Tram_6
    stop_code: '9505'
    route_code: '32009505' 
    show_future_departures: True

Created a pull request to include multiple future departures sensors as an option. Please review and test the code.

1 Like

Thanks, we’ll do! :slight_smile:

Please wait with my pull request. I realize now that my new code could be way more efficient. I’ll create a new pull request tomorrow.

EDIT: Created a new pull request. Changed the code to a more efficient sensor. Also fixed the bug mentioned in issue #3

Thank you for your additions, I have the latest version in test. Looks good, good work!!

I’ll update the docs in the weekend and have a look to create the filter option for stops where more public lines are stopping (multiple tram lines).

1 Like


Thanks for your hard work! But i see that there is no areacode for my bus stop.
See: http://v0.ovapi.nl/line/QBUZZ_g554_1
My busstop is: Edzemaheerd
But when i use the TimingPointCode i have the required information, see: http://v0.ovapi.nl/tpc/10155690

Any idea if this is no much work to build it in the script. I read that all stops has a TimingPointCode, but the areacode is not much used.

With best regards,


That is easy to implement I think, looking at the code now.

I will also suguest to update the documentation:

To find the TimingPointCode (TimingPointCode)

  1. First we need to get an overview of all the lines where we can search in, go to: refer to the JSON response of: v0.ovapi.nl/line/

You see here all the bus lines. Search you line with the right destination like (I was searching for a Bus from Beijum):

{"LineWheelchairAccessible":"ACCESSIBLE","TransportType":"BUS","DestinationName50":"Groningen Beijum via HS - Grote Markt","DataOwnerCode":"QBUZZ","DestinationCode":"836","LinePublicNumber":"4","LinePlanningNumber":"g554","LineName":"Beijum - Roden","LineDirection":2},"ARR_28147_2":{"LineWheelchairAccessible":"NOTACCESSIBLE","TransportType":"BUS","DestinationName50":"Triemen Langelaan","DataOwnerCode":"ARR","DestinationCode":"2726","LinePublicNumber":"7591","LinePlanningNumber":"28147","LineName":"Triemen - Buitenpost, Station","LineDirection":2},"QBUZZ_g554_1":{"LineWheelchairAccessible":"ACCESSIBLE","TransportType":"BUS","DestinationName50":"Roden","DataOwnerCode":"QBUZZ","DestinationCode":"478","LinePublicNumber":"4","LinePlanningNumber":"g554","LineName":"Beijum - Roden","LineDirection":1}

We are searching for the right line number. In the example above the line number is: QBUZZ_g554_1

  1. Now we can find the right TimingPointCode, first we will request all the bus stops for the line number, go to: refer to the JSON response of: v0.ovapi.nl/line/QBUZZ_g554_1

You will see here all the bus stops from that line like (I was searching to the streetname Edzemaheerd):

{"Longitude":6.5902967,"Latitude":53.25269,"TimingPointTown":"Groningen","TimingPointName":"Groningen, Edzemaheerd","TimingPointCode":"10155690","StopAreaCode":null,"TimingPointWheelChairAccessible":"ACCESSIBLE","TimingPointVisualAccessible":"ACCESSIBLE","IsTimingStop":false,"UserStopOrderNumber":5}

Here you will find the TimingPointCode, in the example above it is 10155690.


Created pull request with some bug fixes and support for TimingPointCode.

1 Like

Pippyn has created a update and I’ve approved the Pull request. You can pickup the new version in the repro.

Please let us know if you face any issues.

I can confirm that it is working, i will look if i can submit a pull request for updating the documentation. Maybe this plugin can be a new component for the default components in home assistant ??

I’m really interested in OV Data. Like a train sensor and OV Fiets data.
Since i’m a total noob in python I made a Node-Red Flow for OV Fiets data if someone is interested. It uses http://fiets.openov.nl/locaties.json