Custom Component: Helsinki Regional Transport (HSL HRT) Route Times

Custom bus/tram real time arrival tracking component that extracts data from Helsinki Regional Transport. Realtime data is provided by Digitransit Platform APIs.

Installation Instructions

Code Repository

Do give this a shot and let me know your feedback.

3 Likes

How about extending this to cover also national routes?

Thank you Anand, this is so cool!! A few days ago my daughter asked me to provide this kind of app to her.

About installation. I had difficulties getting the custom_components to show up in Integrations. Changed the execute permission by
chmod 755 *.py
in the installation folder. Donā€™t know if that was necessary. Finally it showed up after reboot of whole server, not just restart of service.

What I would like to have is show the two next ARRIVAL TIMEs for a particular ROUTE (bus) on a particular STOP CODE. I managed easily to show next bus, but how to add a second, like if I miss this one how long do I have to wait?

The route schedule is visible if you click for more info, but I have not figured our the right attribute code.

Another thing, would it be possible to calculate remaining time to arrival on a STOP somehow using e.g. now()?

And Thank You again for your awesome contribution!

Thanks!!

One reason why the custom_components did not show up in Integrations could be the browser cache. But good that you got it working now.

The attributes contain all future arrival times for the day. I will try to build a template to extract next two routes as sensors and see if it works.

For the ā€œremaining timeā€ as well, I think template sensor could work, although comparing times via template has not been easy for me. Will give it a shot and see if I can get something working.

1 Like

It seems there is open data available for national routes as well. Probably that could be a separate sensor. I can try this.

[Edit] A templating error was corrected that resulted in error logs at end of the day, when no more routes were available for the day.

I was able to extract the ā€œnext routesā€ and ā€œremaining timeā€ using a template sensor:

  - platform: template
    sensors:
      next_route_1:
        friendly_name: Next Route - 1
        value_template: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                           {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 0 %}
                             {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[0]['ARRIVAL TIME'] }}
                           {% else %}
                             'No more lines today'
                           {% endif %}
                         {% else %}
                           'No more lines today'
                         {% endif %}"
        attribute_templates:
          route: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                    {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 0 %}
                      {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[0]['ROUTE'] }}
                    {% else %}
                      'Unavailable'
                    {% endif %}
                  {% else %}
                    'Unavailable'
                  {% endif %}"
          destination: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                          {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 0 %}
                            {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[0]['DESTINATION'] }}
                          {% else %}
                            'Unavailable'
                          {% endif %}
                        {% else %}
                          'Unavailable'
                        {% endif %}"
      next_route_2:
        friendly_name: Next Route - 2
        value_template: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                           {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 1 %}
                             {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[1]['ARRIVAL TIME'] }}
                           {% else %}
                             'No more lines today'
                           {% endif %}
                         {% else %}
                           'No more lines today'
                         {% endif %}"
        attribute_templates:
          route: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                    {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 1 %}
                      {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[1]['ROUTE'] }}
                    {% else %}
                      'Unavailable'
                    {% endif %}
                  {% else %}
                    'Unavailable'
                  {% endif %}"
          destination: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') != None %}
                          {% if state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES') | length > 1 %}
                            {{ state_attr('sensor.henttaanaukio_e3313_533', 'ROUTES')[1]['DESTINATION'] }}
                          {% else %}
                            'Unavailable'
                          {% endif %}
                        {% else %}
                          'Unavailable'
                        {% endif %}"
      time_remaining:
        friendly_name: "Time Until Next Arrival"
        value_template: "{% if state_attr('sensor.henttaanaukio_e3313_533', 'ARRIVAL TIME') != None %}
                           {% set curr_time = now().replace(tzinfo=None) %}
                           {% set time_str = state_attr('sensor.henttaanaukio_e3313_533', 'ARRIVAL TIME') %}
                           {% set time_obj = strptime(time_str, '%H:%M:%S') %}
                           {% set right_time = time_obj.replace(year=curr_time.year, day=curr_time.day, month=curr_time.month) %}
                           {% set td = right_time - curr_time %}
                           {{ (td.seconds/3600) | int }}:{{ (td.seconds/60) | int }}:{{ (td.seconds%60) | int }}
                         {% else %}
                           'Unavailable'
                         {% endif %}"

This shows up on dashboard as:

Capture

@Mulperi: Will this help you?

1 Like

Yes, this works well. Thank you so much Anand!

The time_remaining sensor seems to drop the padding zeroes making single digit time a bit difficult to read. In a perfect world it would not show hours (if zero), pad the minutes and update every second. Probably that is not sensible due to the way the app refreshes itself every 10 seconds or so.

I edited your time_remaining so that it shows only minutes until next bus.
unit_of_measurement: 'min'
...
{{ (td.seconds/60) | round(0) }}

However, this probably doesnā€™t work if the next bus arrives in over an hour.

I am very thankful for your support. Great work and very useful Custom Component.

1 Like

Another way to pad the whole timestamp would be to use:

{{ td.seconds | int| timestamp_custom('%H:%M:%S', false) }}

Sample output can be seen under Option-3.

1 Like

I tested your updated plugin (new python file versions).
Can confirm that it now seems to work perfectly.
Big thanks Anand!

1 Like

Thanks @anand-p-r for your component, I recently moved to Helsinki and I find it very usefull.

It would be nice if you can add your component to HACS Community, because the installation procedure itĀ“s a lot easier. It would be possible for you?

Thanks in advance!!!

@IsaTTeN Sure, I will try and give it a shot soon.

Thanks @anand-p-r !!!

Just FYI. I am waiting for the custom integration spec to be approved by someone, as part of HACS submission.

Excellent!

In my opinion the process of installation and updating the custom components is much simpler using HACS.

I hope they will approve soon your component. I canā€™t wait!

See you in Helsinki! Best regards

busstops

Really nice! Do you have any idea how i can make something like a picture? I can get next arrival times, but how i get next bus route (and next after tharā€¦ after thatā€¦).

@Revolver: Can you check the latest release. I have updated it with UI-Option-4 that has a table with the list of scheduled lines on the route. Hope this helps!

2 Likes

Good job! I tried the UI-Option-4 and found that not all buses were visible. Looks like at least two buses are missing. I also noticed that those buses that run the next day side donā€™t show up (like buses running immediately after midnight). But like i said, good job. This is awesome!

Thanks!! Issue with the missing buses is weird. I need to check that.
I have limited the number of buses to a single day in the integration. So buses past midnight will only show up after 00:00. Maybe I can remove that restriction as well. Will get back.

1 Like

Hello anand,

Thanks alot for a great integration!

I encountered some problem with the option 4 arrivals: For some reasons the card does not show the upcoming lines correctly.
Juna2

  • The first upcoming train in the table is not actually the next one arriving in the schedule. It is always third one (2 trains before).

  • Also as you can see I cannot get always the route neither destination to the table.

I used exactly the same configurations as in the example, but of course my stop is H0082 (Pohjois-Haaga train station). Not sure where is the problem, I can get all the schedules to be shown in the option 2 card type. Any ideas what I should try next?

Also I am not sure if the API provides the rail info for the trains?

Hi @Revolver @Art1,
I am not able to the see the ā€œUnavailableā€ / missing lines problem with the current version. I have setup this integration

And I have also setup an automation to track ā€œUnavailableā€. Lets see how it pans out. You could also maybe double check there are no syntax issues with the template you are using?