Add Leading Zeros to numbers?

I have a sensor template set up for the OctoPrint component:

sensor:       
 - platform: template
   sensors:  
     hevo_3d_printer_time_elapsed_friendly:
       value_template: >- 
         {%- set time = states('sensor.hevo_3d_printer_time_elapsed') | int -%}
         {%- set seconds = (time % 60) | int -%}
         {%- set minutes = ((time % 3600) / 60) | int -%}
         {%- set hours = ((time % 86400) / 3600) | int -%}
         {{ hours }}:{{ minutes }}:{{ seconds }}

And it converts the sensor from seconds into Hours, Minutes and Seconds.
But I’d really like it to pad with leading zeros like a real timer.
So instead of:
1:5:7
It would be:
01:05:07
And when it was already 2 digits, there would be no zero padding:
01:45:23

I’ve been Googling for a few hours, but all I can find deals with date formats like now() and not a number that is just seconds.

Any ideas?

sensor:       
 - platform: template
   sensors:  
     hevo_3d_printer_time_elapsed_friendly:
       value_template: >- 
         {%- set time = states('sensor.hevo_3d_printer_time_elapsed') | int -%}
         {%- set seconds = (time % 60) | int -%}
         {%- set minutes = ((time % 3600) / 60) | int -%}
         {%- set hours = ((time % 86400) / 3600) | int -%}
         {{ '{:02}:{:02}:{:02}'.format(hours, minutes, seconds) }}
3 Likes

Beautiful!
Thank you!
I had no idea what to look for.

Yeah, it also depends on the component whether the leading zeros are needed. Sometimes, it doesn’t need them sometimes it does. Sort of a pain. Whenever you need to format strings (or do anything complicated in templates), google ‘jinja2 xxx’ replacing xxx with whatever you want to do.

Jinja2 - that’s what it uses!
Excellent! I probably saw that before and it went right over my head.
Thanks!

Not to step on @petro’s toes, but if sensor.hevo_3d_printer_time_elapsed gives you a number of seconds, why not use instead:

sensor:       
 - platform: template
   sensors:  
     hevo_3d_printer_time_elapsed_friendly:
       value_template: '{{ states("sensor.hevo_3d_printer_time_elapsed") | int | timestamp_custom("%H:%M:%S", false)}}'
2 Likes

I mean, if that’s a timestamp that’s the best way to go.

Its number of seconds since the OctoPrint starting printing.
I think timestamp_custom is expecting an Epoch time. I tried it and the results were all funky.
Thanks.

Come to think of it, I guess timestamp_custom should have worked.
I’m confused.
I’ll try it again.

Ok. I must have done something stupid the first time I tried timestamp_custom.
It works just fine with the elapsed and remaining seconds from the OctoPrint component.
Thank everyone!

1 Like

Ok, last update…maybe.
Ran into a problem where the elapsed time could be longer than 24 hours.
If I use the timestamp_custom function, then I need to include a day part (%j). But that starts on 1, not 0.
So an elapsed time = 0 is formatted as:
001:00:00:00
So I guess I’ll go back to petro’s solution for now and add a ‘set days’ to the value_template, or just use {03} for the hours format so I can handle up to 999 hours.
Thank again everyone!

%j would be the day of the year so yes it’ll never be 0, plus it would only go up to 365.
I’ve not found a formatting for days so you may indeed need to go back to your formula. Ah well at least I tried… :wink:

1 Like

Sorry to awake an old thread, but just found it searching for a similar solution.
I believe a combination of “timestamp_custom” and “days” is the best of both worlds.
You probably dont need to zero-pad the number of days, and don’t need to worry about zero-padding the hours and minutes.

So this is what I ended up with:

          {% set days = (seconds / 86400) | int %}
          {% if days > 1 %}
            {% set time_to_full = days ~ " days, " ~ seconds | timestamp_custom("%H:%M", false) %}
          {% elif days > 0 %}
            {% set time_to_full = days ~ " day, " ~ seconds | timestamp_custom("%H:%M", false)  %}
          {% else %}
            {% set time_to_full = seconds | timestamp_custom("%H:%M", false) %}
          {% endif %}

You know there’s a format for days? You don’t need to concatinate like that.

Format for Days ?
you mean %j ? or %d ?

I ‘used’ this in a last changed sensor but it got hinky if it went beyond a year (it wraps round) (the sensor is not that old, I just believe in testing thoroughly)

I finally went with : -

{% set dif = as_timestamp(now()) - as_timestamp(states('input_datetime.id_heat_last_off')) %}
{% set ds = (dif // (24*60*60)) | int %}
{% set sdif = dif | timestamp_custom('%H:%M', false) %}
{{ ds ~ ' days ' ~ sdif }}

This shows how long ago it happened.
Typical output : 7 days 03:59
No doubt it could be spruced up a bit.

From memory %j defaults to 1 if it just happened, so you have to deduct one or something, can’t remember :thinking:

That is right. “%j” starts at 1 as also mentioned earlier in the thread, so you would need something like "timestamp_custom("%j") | int - 1" which I think is less readable than what you and I are doing.

I was just referencing this code from @Olen

          {% set days = (seconds / 86400) | int %}
          {% if days > 1 %}
            {% set time_to_full = days ~ " days, " ~ seconds | timestamp_custom("%H:%M", false) %}
          {% elif days > 0 %}
            {% set time_to_full = days ~ " day, " ~ seconds | timestamp_custom("%H:%M", false)  %}
          {% else %}
            {% set time_to_full = seconds | timestamp_custom("%H:%M", false) %}
          {% endif %}

which could be simplified to this without the concatenation

          {% set days = (seconds / 86400) | int %}
          {% if days > 1 %}
            {% set time_to_full = seconds | timestamp_custom("%j days %H:%M", false) %}
          {% elif days > 0 %}
            {% set time_to_full = seconds | timestamp_custom("%j day %H:%M", false)  %}
          {% else %}
            {% set time_to_full = seconds | timestamp_custom("%H:%M", false) %}
          {% endif %}

And if you’re feeling saucy…

          {% set days = (seconds / 86400) | int %}
          {% set plural = 'days' if days > 1 else 'day' %}
          {% set fmat = '%j {} %H:%M'.format(plural) if days > 0 else "%H:%M" %}
          {% set time_to_full = seconds | timestamp_custom(fmat, false)  %}

petro, putting all three in the templating tool, thus : -

{% set seconds = 24 * 60 * 60 %}

{% set days = (seconds / 86400) | int %}
{% if days > 1 %}
  {% set time_to_full = days ~ " days, " ~ seconds | timestamp_custom("%H:%M", false) %}
{% elif days > 0 %}
  {% set time_to_full = days ~ " day, " ~ seconds | timestamp_custom("%H:%M", false)  %}
{% else %}
  {% set time_to_full = seconds | timestamp_custom("%H:%M", false) %}
{% endif %}
{{ time_to_full }}

{% set days = (seconds / 86400) | int %}
{% if days > 1 %}
  {% set time_to_full = seconds | timestamp_custom("%j days %H:%M", false) %}
{% elif days > 0 %}
  {% set time_to_full = seconds | timestamp_custom("%j day %H:%M", false)  %}
{% else %}
  {% set time_to_full = seconds | timestamp_custom("%H:%M", false) %}
{% endif %}
{{ time_to_full }}

{% set days = (seconds / 86400) | int %}
{% set plural = 'days' if days > 1 else 'day' %}
{% set fmat = '%j {} %H:%M'.format(plural) if days > 0 else "%H:%M" %}
{% set time_to_full = seconds | timestamp_custom(fmat, false)  %}
{{ time_to_full }}

Produces : -

1 day, 00:00
002 day 00:00
002 day 00:00

So I don’t think we are quite there yet. All three just use (to test) the 1 day (86400 seconds)

The best I can come up with is : -

{% set days = (secs / (24 * 60 * 60)) / int %}
{% set plural = ' days ' if days > 1 else ' day ' %}
{% set hrmn = secs | timestamp_custom('%H:%M', false) %}
{{ days ~ plural ~ hrmn if days > 0 else hrmn }}

1 day 00:00

Edit: I changed the above back to using int when ‘some’ of my sensors started displaying (say) : -
1.0 day 00:00

Yeah, I’m mixing up timedelta’s and datetime.strftimes. Timedelta has the correct day number. Just ignore my post, you’ll have to concatenate.

No, the point is “We Tried
Always “butt in” if you have a different perspective
:+1:

@Olen , @lolouk44
I used the // rather than int and (see post above) expanded the hrs-mins-secs (just to make it obvious and easier to read)
I know we are guilding the lilly here but any input welcome

Olen, not everyone wants the comma but it’s easy to put back in :rofl:

1 Like