I was just looking to do this myself, thanks for the example!
You’ve got 4 of these…
{% else %}
{% set uptime = uptime %}
Which I don’t think you need. So like the hours one you’ve got
{% if hours | int > 0 %}
{% set uptime = uptime ~ ', ' %}
{% else %}
{% set uptime = uptime %}
{% endif %}
When I think just
{% if hours | int > 0 %}
{% set uptime = uptime ~ ', ' %}
{% endif %}
Achieves exactly the same thing.
Yes, I think you’re right.
Thanks.
I must have been referring back to the time when I had scripts without an else. Rather than continuing processing if the ‘if’ failed they just stopped completely so I had to create a script that I could call that did nothing. Hence an else in every case here
Actually, I don’t think I ned any of them and nor do I need the initial
{% else %}
{% set uptime = '' %}
Not sure, I’m having a bit of a dyslexia moment trying to work out what the uptime variable will be at each stage of the process, I would pull the ones you know you can take and test it’s working and then just comment out the other bits for testing
I can well understand the dyslexia moments. I was having them when I was putting this together which was really why I wondered if there was a more elegant solution.
uptime
should only start reporting the length of time beginning with the largest non zero unit, it inserts commas as appropriate and adds an ‘s’ to the end of the unit descriptor if it is greater than one.
I tried in the template dev page and it seems to work without any of the else
s.
I’d probably do something like this:
- platform: template
sensors:
ha_uptime:
friendly_name: HA Uptime
value_template: >
{%- macro phrase(value, name) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value, name, end) if value > 0 else '' }}
{%- endmacro %}
{%- set weeks = (states('sensor.uptime') | int / 7) | int %}
{%- set days = (states('sensor.uptime') | int) - (weeks * 7) %}
{%- set hours = (states('sensor.uptime') | float - states('sensor.uptime') | int) * 24 %}
{%- set minutes = (hours - hours | int) * 60 %}
{{ [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), phrase(minutes, 'min') ] | select('!=','') | join(', ') }}
Different outputs:
4 weeks, 3 days, 2 hrs, 1 min
4 weeks, 2 hrs, 1 min
4 weeks, 1 min
1 week, 1 day, 1 hr, 5 mins
Basically, it should work all the time and its shorter.
Nice.
I nearly put @petro in my original post but shied away thinking you’d respond if you wanted too.
However, and I can’t believe I am correcting you, I think you missed a few | int
in the last line
{{ [ phrase(weeks | int, 'week'), phrase(days | int, 'day'), phrase(hours | int, 'hr'), phrase(minutes | int, 'min') ] | select('!=','') | list | join(', ') }}
Now I 'm off to spend some time working out exactly how this line
{{- '{} {}{}'.format(value, name, end) if value > 0 else '' }}
works
Actually, you don’t need | int
there! It just passes the number, string, or float.
And the reason that is is because this line:
{{- '{} {}{}'.format(value, name, end) if value > 0 else '' }}
That line is the format line. It replaces the first {} with value, the second {} with name, and the third {} with end.
So if value is 4, name is ‘foo’, and end is ‘s’, the result would be
4 foos
if value is 4.82342, name is ‘petro’, and end is ‘s’, the result would be
4.82342 petros
Now looking at the line as a whole:
{{- '{} {}{}'.format(value, name, end) if value > 0 else '' }}
We have an if else statement built in there. It can be confusing but this is how you should look at it:
{{ TRUE if STATEMENT else FALSE }}
So if the statement is true, TRUE is executed.
if the statement is false, FALSE is executed.
So in our original line, our TRUE is '{} {}{}'.format(value, name, end)
, our statement is value > 0
, and our false is ''
.
So if the value is greater than zero, output a formatted string, otherwise output nothing.
That great, thanks.
One question though, what precisely does the -
after the {{
do in that statement?
and also without the | int
I get this in the template editor page
whereas with them I get
Ah, yes didn’t catch that. Days, hours, and minutes would need the int. Weeks is already an int. But we can fix that in the macro:
{%- macro phrase(value, name) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value | int, name, end) if value > 0 else '' }}
{%- endmacro %}
{%- set weeks = (states('sensor.uptime') | int / 7) | int %}
{%- set days = (states('sensor.uptime') | int) - (weeks * 7) %}
{%- set hours = (states('sensor.uptime') | float - states('sensor.uptime') | int) * 24 %}
{%- set minutes = (hours - hours | int) * 60 %}
{{ [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), phrase(minutes, 'min') ] | select('!=','') | join(', ') }}
what about…
Thanks for all this. It is very enlightening.
Oh, that removes whitespace so that the output looks nice in the template editor. It’s not necessary in templates themselves because whitespace is removed by nature but it is needed when sending messages via notify because the messages do not remove whitespace. Either way its good practice to know when you need to remove whitespace.
For shits and giggles, just remove those dashes and see what it looks like in the template editor.
hmmm something must be amiss:
my time_online is:
and time since last_boot is:
yet the macro says more than 7 weeks…:
# https://home-assistant.io/components/sensor.uptime/
- platform: uptime
name: Time Online
unit_of_measurement: hours
Ignore this, sorry… HUGE typo and Copy, Paste error…
Another fix to the macro:
{% macro phrase(value, name) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value | int, name, end) if value | int > 0 else '' }}
{%- endmacro %}
Also, My output is saying 14 minutes. Is that not what you want?
That’s because @klogg s uptime sensors units is days, not hours like yours. You’ll need to fix your math to account for the fact that your sensor is based in hours.
Yes, sorry, I had a huge brain malfunction with a copy, paste error.
The macro fix works perfectly (of course).
The other part of my post which I so rudely deleted whilst you were replying was saying that for the first 14 minutes this returns an empty string so for completeness I have added this (unless you know a better way )
{% if states('sensor.uptime') == '0.0' %}
Just restarted...
{% else %}
{% macro phrase(value, name) %}
{%- set end = 's' if value > 1 else '' %}
{{- '{} {}{}'.format(value | int, name, end) if value | int > 0 else '' }}
{%- endmacro %}
{% set weeks = (states('sensor.uptime') | int / 7) | int %}
{% set days = (states('sensor.uptime') | int) - (weeks * 7) %}
{% set hours = (states('sensor.uptime') | float - states('sensor.uptime') | int) * 24 %}
{% set minutes = (hours - hours | int) * 60 %}
{{ [ phrase(weeks, 'week'), phrase(days, 'day'), phrase(hours, 'hr'), phrase(minutes, 'min') ] | select('!=','') | list | join(', ') }}
{% endif %}
A yes, missed that. Won’t tamper with the macro. Added another sensor
At the risk of getting boring I think we also need to amend
{%- set end = 's' if value > 1 else '' %}
with another | int
to be
{%- set end = 's' if value | int > 1 else '' %}
else we get an ‘s’ whenever the value is between 1 and 2