Time template for bus arrival in x minutes

I am trying to figure out how to do this, but even though I read a lot in the forums or the docs, I couldnt make it work…
I am not good at templating but I am even worse in time (which I believe it is a special learning skill by itself)…
Anyway, I am having a sensor for my bus arrival with the state showing like this:

2019-05-08T11:09:27.259792+00:00

What I am trying to do, is have a message being sent to me, notifying me about the next arrival time (when the state changes) or when it last passed by.

I have come with this template

{{ (as_timestamp(states.entity_id.state) - as_timestamp(now()) ) | timestamp_custom(‘%H:%M’) }}

Which returns this:

01:57

The above result means the bus passed by the stop 3 minutes ago.

This one:

02:05

Means the bus will arrive in 5 minutes.

I know the template I made is too simple, but that is how far I can go. Ugh… I am bad at this.
Please help me with this.

Edit: I also want to filter the time and have it give me only a number like:
The bus will arrive in 5 minutes.
or
The bus passed by 3 minutes ago.

Can the template give a true or false state also? That way I can make the automation change the message depending on if the bus is coming or leaving the stop.

you need to use the false parameter in your timestamp_custom to ensure the time is local to you and not UTC.
This is what I use for my setup. In this instance, a negative time would be the bus had already passed.
{{ (as_timestamp(now()) - as_timestamp(states.input_boolean.xiaomi_switch_charging.last_changed | default(0)) | int ) | timestamp_custom("%Hh %Mm", false)}}
This gives me for example 2h 5m.
If you’re always going to have minutes and never hours, then just use timestamp_custom("%Hh %Mm", false)

1 Like

Try

  bus_arrival:
    friendly_name: "bus arrival"
    value_template: >-
      {% set minutes = ( as_timestamp(states.entity_id.state) - as_timestamp(now()) ) | timestamp_utc %}
      {{ strptime(minutes, '%Y-%m-%d %H:%M:%S').strftime('%M') }} min
1 Like

Ok this is alittle long winded but it should give you what you are looking for. This assumes your sensor name is sensor.bus_arrival. Just change the entity_id and you should be good to go. Also, you need to have the time_date platform sensor to update this every minute, so that is added in the yaml. You don’t need it if you already have this integrated.

sensor:
  # Time for updates to template sensor
  - platform: time_date
    display_options:
      - 'time'

  # template sensors
  - platform: template
    sensors:
      bus_arrives_in:
        friendly_name: Countdown To Bus
        entity_id: sensor.time
        value_template: >-
          {% set bus_arrival = states('sensor.bus_arrival') %}
          {% set time = as_timestamp(bus_arrival) - now().timestamp() %}
          {%- macro gettimestring(tstring, number) %}
          {%- if number > 0 %}
          {%- set ret = '{} {} ' if number == 1 else '{} {}s ' %}
          {{- ret.format(number, tstring) }}
          {%- else %}
          {{- '' }}
          {%- endif %}
          {%- endmacro %}
          {% if time < 0 %}
            {% set time = time * -1 %}
            {% set phrase = 'The bus passed by {}ago' %}
          {% else %}
            {% set phrase = 'The bus will arrive in {}' %}
          {% endif %}
          {%- set minutes = gettimestring('minute', ((time % 3600) / 60) | int ) %}
          {%- set hours = gettimestring('hour', ((time % 86400) / 3600) | int ) %}
          {%- set days = gettimestring('day', (time / 86400) | int ) %}
          {%- set ret = phrase.format('less than 1 min ') if time < 60 else phrase.format(days + hours + minutes) %}
          {{ ret.strip()+'.' }}

image
image
image
image
image
image
image
image
image

2 Likes

here is something that might help a little in understanding time in Linux:

1 Like

Thank you all guys for your help!
I went for Petro’s suggestion and it works like a charm.

I want to use the same template for the next arrival. But there is a problem.
The next arrival is an attribute in the sensor (sensor.bus_arrival.attributes.second_next_arrival)
When there is no other bus coming after, the attribute is gone. So it has no state. It is not even “unknown”.

Can you tweak the template so it can tell if there is a next bus incoming or not? And if it is coming, it should say in how much time.

I am thinking the output to be something like this:

The bus is coming in 5 minutes. There is a next one in 29 minutes.

The bus is coming in 5 minutes. There will not be another one after that.

Can you post your full template and I’ll take a look at it.

I have copy-pasted yours from earlier. Havent changed anything but the entityid. :smile:

take a pic of the entity in the states page

I hid some attributes for privacy.

EDIT: The “second_next_arrival” is the one that is going away if there is no other bus on the way. I mean, it is completely gone.

Is next arrival time what you used in the template? Or second?

The state and the next_arrival are the same time, so I dont think it matters. But anyway I am using the state. This tells me when the bus is coming.
Now, if I use the second_next_arrival it will tell me when the bus AFTER that (in the state) will come.
So, my goal here is to message me when the second bus will arrive. And if there is no bus, then just ignore it or just say “no other busses are on the way”

Like my example earlier:

The bus is coming in 5 minutes. There is a next one in 29 minutes.

The bus is coming in 5 minutes. There will not be another one after that.

Alright, and when there is no second bus, does the attribute exist or is it empty?

The attribute is gone. It doesnt exist at all.
I tried some templating using ‘if variable is defined’, but I couldnt figure it out.
Thank you very much for your effort!

Give this a whirl. I had to change the sentence structure to keep this compact.

sensor:
  # Time for updates to template sensor
  - platform: time_date
    display_options:
      - 'time'

  # template sensors
  - platform: template
    sensors:
      bus_arrives_in:
        friendly_name: Countdown To Bus
        entity_id: sensor.time
        value_template: >-
          {% set bus_arrival = state_attr('sensor.bus_arrival', 'next_arrival') %}
          {% set second_bus_arrival = state_attr('sensor.bus_arrival', 'second_next_arrival') %}
          {% set time = as_timestamp(bus_arrival) - now().timestamp() if bus_arrival != None else None %}
          {% set time2 = as_timestamp(second_bus_arrival) - now().timestamp() if second_bus_arrival != None else None %}
          {%- macro gettimestring(tstring, number) %}
          {%- if number > 0 %}
          {%- set ret = '{} {} ' if number == 1 else '{} {}s ' %}
          {{- ret.format(number, tstring) }}
          {%- else %}
          {{- '' }}
          {%- endif %}
          {%- endmacro %}
          {%- macro getphrase(time, name) %}
          {%- if time != None %}
            {%- if time < 0 %}
              {%- set time = time * -1 %}
              {%- set phrase = 'The {} bus passed by {}ago' %}
            {%- else %}
              {%- set phrase = 'The {} bus will arrive in {}' %}
            {%- endif %}
            {%- set minutes = gettimestring('minute', ((time % 3600) / 60) | int ) %}
            {%- set hours = gettimestring('hour', ((time % 86400) / 3600) | int ) %}
            {%- set days = gettimestring('day', (time / 86400) | int ) %}
            {%- set ret = phrase.format('less than 1 min ') if time < 60 else phrase.format(name, days + hours + minutes) %}
            {{- ret.strip()+'.' }}
          {%- else %}
            {{- 'There will not be a {} bus.'.format(name) }}
          {%- endif %}
          {%- endmacro %}
          {{ getphrase(time, 'first') }}  {{ getphrase(time2, 'second') }}