Convert seconds to days, hours, minutes

Have tried to follow along and adapt to my situation with mixed results.
I have an SMNP uptime sensor that returns a timeticks value.

I wanted to achieve the same results as above but instead get this:

Here is my code, noting that timeticks need to be converted to seconds/minutes/hours with a different calculation that normal time

- platform: template
  sensors:
  esxi_real_uptime:
    friendly_name: ESXi Uptime
    value_template: >-
      {% set time = states.sensor.esxi_raw_uptime.state | int %}
      {% set minutes = (time / 6000) | int%}
      {% set hours = (time / 360000) | int %}
      {% set days = (time / 8640000) | int %}

      {%- if time < 6000 -%}
        Less than a minute
      {%- else -%}
        {%- if days > 0 -%}
          {%- if days == 1 -%}
            1 day
          {%- else -%}
            {{ days }} days
          {%- endif -%}
        {%- endif -%}
        {%- if hours > 0 -%}
          {%- if days > 0 -%}
            {{ ', ' }}
          {%- endif -%}
          {%- if hours == 1 -%}
            1 hour
          {%- else -%}
            {{ hours }} hours
          {%- endif -%}
        {%- endif -%}
        {%- if minutes > 0 -%}
          {%- if days > 0 or hours > 0 -%}
            {{ ', ' }}
          {%- endif -%}
          {%- if minutes == 1 -%}
            1 minute
          {%- else -%}
            {{ minutes }} minutes
          {%- endif -%}
        {%- endif -%}
      {%- endif -%}

Not totally sure where I am going wrong here. Spent about 3 hours trying to figure it out with no luck.

Gah, I must be tired. I went back one last time after posting this and found my mistake.

Have to set the actual values for minutes and hours:

      {% set time = states.sensor.esxi_raw_uptime.state | int %}
      {% set minutes = ((time % 360000) / 6000) | int%}
      {% set hours = ((time % 8640000) / 360000) | int %}
      {% set days = (time / 8640000) | int %}

Much better:

Another (quick) way of converting seconds to a (sort of) nice format:

value_template: “{{ ((value | int) / 100) | int | timestamp_custom(‘%j days %H:%M:%S’) }}”

In this case value is in hundred of seconds, hence the value/100.

Addendum: discovered one drawback, days is always one to many because day 0 doesnt exist and datetime starts from day 1.

2 Likes

A further enhancements which hasn’t got the drawback of wrong number of days:

  value_template: >-
    {% set uptime = (value | int) / 100 | int %}
    {% set days = (uptime / 86400) | int %}
    {%- if days > 0 -%}
      {{ days }} days, {{ (uptime - (days * 86400)) | int | timestamp_custom('%H:%M:%S') }}
    {%- else -%}
      {{ uptime | int | timestamp_custom('%H:%M:%S') }}
    {%- endif -%}
2 Likes

I was using the original code but i wanted to shorten the ouput so i modified the code to output 00d 00h 00m. Much better IMO than the elongated 00 days, 00 hours, 00 minutes.

Heres the code:

value_template: >-
{% set time = (value | int) | int %}
{% set minutes = ((time % 360000) / 6000) | int %}
{% set hours = ((time % 8640000) / 360000) | int %}
{% set days = (time / 8640000) | int %}

{%- if time < 6000 -%}
  Less than a minute
{%- else -%}
  {%- if days > 0 -%}
    {{ days }}d
  {%- endif -%}
  {%- if hours > 0 -%}
    {%- if days > 0 -%}
      {{ ' ' }}
    {%- endif -%}
    {{ hours }}h
  {%- endif -%}
  {%- if minutes > 0 -%}
    {%- if days > 0 or hours > 0 -%}
      {{ ' ' }}
    {%- endif -%}
    {{ minutes }}m
  {%- endif -%}
{%- endif -%}
2 Likes

To contribute my version: it abuses Jinja’s whitespace control. Easily extendable to Months, Quarters,Years, etc.

edit //
It was breaking on exactly 60 minutes, 1 day, etc. Should be more robust now!

  # NOTE:
  # - define durations for the various UNITs of time
  #   * modulus helps to reduce "extras" of the larger time units
  # 
  # - now build the string
  #
  # for each UNIT of time that's greater than 1:
  #     if it's not the first loop iteration:
  #         append ", " to the string
  #     else:
  #         1. convert the DURATION to STR
  #         2. split DURATION on "."
  #         3. append the left part (the whole number) to the string
  #         4. append the UNIT to the string
  #
  # ...but if that's no UNITs, then we just started so
  # set the string to "just now"
  #
  - platform: template
    sensors:
      hass_uptime:
        value_template: >-
          {%- set uptime  = states.sensor.hass_uptime_minutes.state | round -%}
          {%- set sep     = ', ' -%}
          {%- set TIME_MAP = {
              'week': (uptime / 10080) % 10080,
               'day': (uptime / 1440) % 7,
              'hour': (uptime / 60) % 24,
            'minute': (uptime % 60)
          }
          -%}

          {%- for unit, duration in TIME_MAP.items() if duration >= 1 -%}
            {%- if not loop.first -%}
              {{ sep }}
            {%- endif -%}
              
            {{ (duration | string).split('.')[0] }} {{ unit }}

            {%- if duration >= 2 -%}
              s
            {%- endif -%}
          {%- endfor -%}

          {%- if uptime < 1 -%}
            just now
          {%- endif -%}
9 Likes

much better than the hacky version I was using which didnt display properly. Thanks for sharing :hugs:

Hi
i want ask will this code work with NUT ?

i want make the Runtime in minute or hour will work ?
i never use template before

thank you

Yes, I am using essentially the same code for mine.

- platform: template
  sensors:  
    nut_ups_runtime_friendly:
    friendly_name: 'Time Remaining'
    value_template: >- 
      {% set time = (states.sensor.nut_ups_battery_runtime.state | int) | int %}
      {% set minutes = ((time % 3600) / 60) | int %}
      {% set hours = ((time % 86400) / 3600) | int %}
      {% set days = (time / 86400) | int %}
    
      {%- if time < 60 -%}
        Less than a minute
        {%- else -%}
        {%- if days > 0 -%}
          {{ days }}d
        {%- endif -%}
        {%- if hours > 0 -%}
          {%- if days > 0 -%}
            {{ ' ' }}
          {%- endif -%}
          {{ hours }}h
        {%- endif -%}
        {%- if minutes > 0 -%}
          {%- if days > 0 or hours > 0 -%}
            {{ ' ' }}
          {%- endif -%}
          {{ minutes }}m
        {%- endif -%}
      {%- endif -%}
2 Likes

thanks for reply
it don’t work for me don’t know why ?
do i need to change the code or not ?

thank you

Yes, you will need to change sensor.nut_ups_battery_runtime to match whatever your sensor is but that should be all that’s needed

hello

sorry take my long time to try this i was busy i try change the sensor.nut_ups_battery_runtime but still not working my name same as you sensor.nut_ups_battery_runtime it give this error ?

thank you

If you’re using the exact same configuration as shown in @LJM9000’s post above: The indentation is wrong; all lines from friendly_name onwards must be indented by 2 more spaces.

1 Like

OK i try like you say the now give Configuration valid! but the time still in second

And have you changed your user interface configuration? That code does not change the way the original sensor stores and represents its data, it’s creating a new sensor.
In the entities card that currently displays all data about your UPS you will have to change the entity ID from sensor.nut_ups_battery_runtime to sensor.nut_ups_runtime_friendly.

1 Like

thank you exxamalte now is working
and thank you all

:smile::smile:

1 Like

why 10080 ?
Isn’t 1 week = 60sec * 60 min * 24h * 7day = 604800 ?

The uptime in that template sensor is given in minutes, not seconds.

This feels like a more accurate version :

          {%- set TIME_MAP = {
                        'w': (uptime / 604800) % 604800,
                        'd': (uptime / 86400) % 7,
                        'h': (uptime / 3600) % 24,
                        'm': (uptime / 60) % 60,
                        's': (uptime % 60)
                    }
          -%}
2 Likes

Certainly more accurate, but unnecessary for my needs. I believe the sensor will only update every 15 seconds anyway?

Mostly, the way I think of it is … “when will I ever care about the exact second my HASS instance started?” It’s mostly a for-fun sensor anyway. :slight_smile: