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.
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)
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()+'.' }}
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.
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.
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') }}