Converting history_stats from hours to minutes and seconds

I’ve been fighting with this all day and can’t seem to get this completely worked out. Each one of these is close, but doesn’t fully resolve. Plus, on the last two, I don’t even fully understand the syntax.

The history_stats integration reported that the Tree Sprinklers ran today for .14 hours. Converted to minutes, that’s 8.4 minutes. Converted to minutes/seconds, that’s 8 mins, 24 seconds.

My attempts and issues are below:

The first one doesn’t work because it doesn’t parse out seconds from minutes. The split(.) is required. Not really sure how to implement this here. Hell, this doesn’t even give the correct answer.

sensor:
  - platform: template
    rainmachine_convert_hh_to_mm_ss:
      unit_of_measurement: 'Mins'
      value_template: {{ (states('sensor.side_yard_runtime_today') | float * 60) }}

The second one doesn’t work because it does the same math to the number in the tens place. This resolves to 8 min, 8 seconds, which is also wrong.

sensor:
  platform_template:
      rainmachine_convert_side_yard:
        friendly_name: "Tree Sprinklers Duration"
        entity_id: sensor.tree_sprinklers_runtime_today
        value_template: >
          {% set time = (states('sensor.tree_sprinklers_runtime_today')) %}
          {% set minutes = ((time | float * 60) | string).split('.')[0] %}
          {% set seconds = minutes | int % 60| round(0) %}
          {{minutes}} Minutes {{seconds}} Seconds

The last one doesn’t work because the number in the tens, hundreds, and thousands place is saying 240 seconds, not 24 seconds. This would work, but round(2) does not seem to be working (at least in the template configurator).

sensor:
  - platform: template
    sensors:
      rainmachine_convert_side_yard:
        friendly_name: "Tree Sprinklers Duration"
        entity_id: sensor.tree_sprinklers_runtime_today
        value_template: >
          {% set time = states('sensor.tree_sprinklers_runtime_today') | float %} 
          {% set minutes = ((time %1) * 60) | int %}  
          {% set seconds = ((minutes %9) * 60 /2 ) | float | round(2) %}
          {{minutes}} Minutes {{seconds}} Seconds

Finally, I don’t even understand some of this. In the second example, I get that I’m setting the time variable to be the state output for the sensor. I believe the leading % and ending % in the curly braces are used to evaluate the statement. I understand that when I set the minutes variable, that I’m taking the time, using float and multiplying by 60 to get the minutes. The output is converted a string and then split using the decimal. However, I have no idea what the [0] does, except when I change it, it halves the result. I also don’t understand the section when I’m setting the seconds variable, what does int % 60 mean (specifically, the %)?

In the third example, where I’m setting the seconds variable, what is ((minutes%9)? Why does that give me a better answer than ((minutes%1)? What is happening here?

At any rate, please help. I’m finally waving the flag - I’m stuck.

1 Like

have you checked out

I think strptime() might be able to help you. Load your original string as hours into the timestamp and get out whatever format you want using strftime().
Just ignore the date part.
see the 3rd post in that thread.

I have not, thanks for the tip, checking it out now.

A simple template solution:

{% set duration_hours = 0.14 %}
{% set duration = duration_hours * 60 %}
{{ duration|int }}:{{ '{:02}'.format(((duration - duration|int) * 60) |int) }}

That returns 8:24.

duration is 8.4 minutes, as previously noted. Probably not a good idea to use time as a variable name.

{{ duration|int }} takes the integer part, which is 8.

To get the seconds, we first get the fractional minutes part (0.4) by subtracting the 8 from the 8.4, then multiply by 60 to convert to seconds: {{ ((duration - duration|int) * 60) }}.

However, you wouldn’t get a leading zero so 8m04s would render as 8:4; and there is likely to be some rounding “noise” — my machine renders the result of the above as 24.00000000000002, for example.

So we then take the int part of that and format with a leading zero:

{{ '{:02}'.format(((duration - duration|int) * 60) |int) }}.

Alternatively, allowing for times greater than an hour:

{% set duration_hours = 0.14 %}
{{ (duration_hours * 3600)|timestamp_custom("%H:%M:%S", False) }}
1 Like

For a standard conversion technique you need to get your value in seconds
so 0.14 hours becomes : -
secs = 0.14 * 60 * 60
So substitute in for your seconds calculation

Then

          {% set secs = as_timestamp(now()) - as_timestamp(states('input_datetime.id_heat_last_off')) %}
          {% set days = (secs / (24 * 60 * 60)) | int %}
          {% set plural = ' days ' if days > 1 else ' day ' %}
          {% set hrmn = secs | timestamp_custom('%H:%M:%S', false) %}
          {{ days ~ plural ~ hrmn if days > 0 else hrmn }}

I pretty much use this for every ‘time since’ or ‘elapsed’ sensor I have (makes it neat :smiley: )

Edited: as you wanted seconds too (I don’t)

1 Like

Use timestamp_custom().

The following screenshot shows four different ways it can be used to display 0.14 hours:

  • hours:minutes:seconds
  • minutes:seconds
  • minutes:seconds (minutes are not zero-padded)
  • presented as a sentence and neither value is zero-padded

Just pick the display format you want, convert the hours to seconds (multiply by 3600), and feed that result to timestamp_custom.

2 Likes

That is called a “modulo” operator. It returns the remainder (“modulus”) of the division expression:

35 % 10 = 5

Post the following in to the template editor for the explanation for why your templates don’t work as you expect:

{{ 35 % 10 }}

--

{%- set time = 0.14 %} 
{% set minutes = ((time %1) * 60) | int %}  
{% set seconds = ((minutes %9) * 60 /2 ) | float | round(2) %}
{{minutes}} Minutes {{seconds}} Seconds


since 8 divided by 9 leaves a remainder of 8 => {{ (8 % 9) }} 

8 times 60 = 480 => {{ (8 % 9) * 60 }} 

480 divided by 2 = 240 => {{ ((8 %9) * 60 /2 ) }} 

it won't round anymore since there is nothing to round => {{ ((8 %9) * 60 /2 ) | float | round(2) }} 

--

{%- set time = 0.14 %}
{% set minutes = ((time | float * 60) | string).split('.')[0] %}
{% set seconds = minutes | int % 60| round(0) %}
{{minutes}} Minutes {{seconds}} Seconds

convert time (0.14) to a float (it already is) & multiply by 60 => {{ (time | float * 60) }} 

then convert it to a string (looks the same but they aren't) => {{ (time | float * 60)  | string }}

{%- set number = (time | float * 60) %}
{% set string = (time | float * 60)  | string %}

they aren't the same even tho they look the same on the screen => {{ number == string }}

returns a list of objects split on the decimal => {{ ((time | float * 60)  | string).split('.') }}

all lists in jinja are 0 index based so the first item will be index [0] so...

returns the first index item item in the list => {{ ((time | float * 60)  | string).split('.')[0] }}

I tried to post a screenshot but it’s not working for me anymore. Anyone else seen this lately?

Thank you so much for this! This is exactly the explanation I needed - tearing the code apart and examining what each command does. I read your post that @datamonkey linked and it was too much to suck in at midnight, so I’m going to give it another stab today.

I see the error in my math and why it didn’t work now. I completely forgot that the modulo operator existed - it’s something that I rarely ever used and only in math class 20+ years ago. I appreciate the usage much more now. It also seems that the way I handled this was a little clunky and would give inconsistent results when applied with different values.

Of the other commenters, I most understood @Troon’s reply, so I ended up utilizing that. It scales well and correctly displays the different results that I needed to convert.

Thank you, this was the solution I was looking for. I appreciate you taking apart the code to help explain it to me (also, you’re right - time as a variable name is probably a bad idea).

Converting everything to seconds makes tons of sense - rather than just trying to break apart what I have, why not create an easier figure to work with?

At any rate, this is a bit complicated, but I see the value.

I don’t know what I was doing wrong, but I could not get any of this to work. Nonetheless, I completely understand this - the conversion to seconds and then working with that makes more sense than what I was doing.

If you used timestamp_custom without setting the second parameter to false then it defaults to true and its resulting behavior will be undesirable for your purpose.

It’s as simple as this:

sensor:
  platform_template:
      rainmachine_convert_side_yard:
        friendly_name: "Tree Sprinklers Duration"
        value_template: >
          {{ (states('sensor.tree_sprinklers_runtime_today') | float * 3600)
             | timestamp_custom('%-M minutes %-S seconds', false) }}

It converts the sensor’s state value to a floating point number, multiplies the result by 3600 to get seconds, then supplies that to timestamp_custom which presents it as minutes and seconds (without zero-padding).

NOTE
If the sensor’s value can exceed 1 hour, you should add hours to timestamp_custom like this:

timestamp_custom('%-H hours %-M minutes %-S seconds', false)

3 Likes

Glad you got it sorted.
By the way, Troons solution will only go up to 23:59:59
Any more than that and it will start ar 00:00:00 again
But if you are okay with that, it’s all good

“I could not get any of this to work”
Why ? Where are you using it, how is it to be employed ?

For THIS purpose, it’s not an issue. In theory, the sprinklers should never be running more than 15 mins each.

I just added a comment to the template indicating as such - that I need to be careful if/when reused for this reason.

For the “I could not get any of this to work” - I was just placing in the template editor before applying to the yaml. I couldn’t get any of it to output a response. I will admit, with so many options available to me, I went with the lowest hanging fruit and didn’t toy with it as much as I could have to actually find out why.

Although I already have the desired output, I’ll keep messing with this. I have to admit that I didn’t spend a ton of time on it (partially because I’m working right now too).