[Solved] Convert `1h 24m` string to minutes

I have following sensor for my vaccum that among other things has attribute value with time how long entity was in particular state

- platform: history_stats
  name: Vacuum cleanups today
  entity_id: vacuum.servantess
  state: 'cleaning'
  type: count
  start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
  end: '{{ now() }}'

Time is available in format xm e.g. 17m and today after vacuum was unusually diligent I discovered value was actually in form yh xm e.g. 1h 24m which broke my automation:

- alias: Send Servantess to dock when someone arrives home during repeated cleanup
  trigger:
    - platform: state
      entity_id: group.people
      to: 'home'
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: vacuum.servantess
        state: 'cleaning'
      - condition: template
        value_template: '{{ states.sensor.vacuum_cleanups_today.state | int > 1 }}'
      - condition: template
        value_template: '{{ states.sensor.vacuum_cleanups_today.attributes.value | replace("m", "") | int | default(0) > 10 }}'
  action:
    - service: vacuum.return_to_base
      entity_id: vacuum.servantess

What would be the best concise and clean way how to convert states.sensor.vacuum_cleanups_today.attributes.value to minutes for comparing?
I did not find a way to control the format how sensor sets time value so value_template should work for both xm and yh xm patterns.

this?

{% if states.sensor.vacuum_cleanups_today.attributes.value| length > 3 -%}
{{ states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[0] | replace('h','') | int  * 60 +  states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[1] | replace('m','') | int }}
{%- else -%}
{{ states.sensor.vacuum_cleanups_today.attributes.value| replace('m','') | int }}
{%- endif %}

Thanks. The idea is right.
I modified your code a bit and now it works in both cases:

- alias: Send Servantess to dock when someone arrives home during repeated cleanup
  trigger:
    - platform: state
      entity_id: group.people
      to: 'home'
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: vacuum.servantess
        state: 'cleaning'
      - condition: template
        value_template: '{{ states.sensor.vacuum_cleanups_today.state | int > 1 }}'
      - condition: template
        value_template: >
          {% if states.sensor.vacuum_cleanups_today.attributes.value | length > 3 %}
            {{ (states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[0] | replace("h", "") | int  * 60 + states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[1] | replace("m", "") | int) > 10 }}
          {% else %}
            {{ states.sensor.vacuum_cleanups_today.attributes.value | replace("m", "") | int | default(0) > 10 }}
          {% endif %}
  action:
    - service: vacuum.return_to_base
      entity_id: vacuum.servantess
1 Like

You could shorten it a bit to be more readable:

change

{{ (states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[0] | replace("h", "") | int  * 60 + states.sensor.vacuum_cleanups_today.attributes.value.split(" ")[1] | replace("m", "") | int) > 10 }}

to

{% set t = strptime(state_attr('sensor.vacuum_cleanups_today','value'), '%Hh %Mm') %}
{{ (t.hour / 60 + t.minute) > 10 }}

And building on the previous suggestions, how about:

      - condition: template
        value_template: >
          {% set s = state_attr('sensor.vacuum_cleanups_today', 'value') %}
          {% set t = strptime(s, '%Hh %Mm' if 'h' in s else '%Mm') %}
          {{ (t.hour * 60 + t.minute) > 10 }}

EDIT: Hmm, just realized, that / should probably be a *, right??? (Updated above accordingly.)

2 Likes

Ah yep, no more need for that if statement, short and concise!

BTW, you don’t need to specify condition: and – that is the default. Also, your second condition would normally be written using the numeric_state condition. So:

  condition:
    - condition: state
      entity_id: vacuum.servantess
      state: 'cleaning'
    - condition: numeric_state
      entity_id: sensor.vacuum_cleanups_today
      above: 1
    - condition: template
      value_template: >
        {% set s = state_attr('sensor.vacuum_cleanups_today', 'value') %}
        {% set t = strptime(s, '%Hh %Mm' if 'h' in s else '%Mm') %}
        {{ (t.hour * 60 + t.minute) > 10 }}

On the other hand, you could do the whole thing in one template condition:

  condition:
    condition: template
    value_template: >
      {% set s = state_attr('sensor.vacuum_cleanups_today', 'value') %}
      {% set t = strptime(s, '%Hh %Mm' if 'h' in s else '%Mm') %}
      {{ is_state('vacuum.servantess', 'cleaning') and
         states('sensor.vacuum_cleanups_today')|int > 1 and
         (t.hour * 60 + t.minute) > 10 }}
1 Like

Thanks @petro and @pnbruckner, looks very cool! Will update automation tomorrow!

I know that it can be done with strptime but could not figure out concise way how to cover both cases but this looks exactly what I need.

Actually in my case since I case only about minutes I could cover both cases by changing

      - condition: template
        value_template: '{{ states.sensor.vacuum_cleanups_today.attributes.value | replace("m", "") | int | default(0) > 10 }}'

to

      - condition: template
        value_template: '{{ states.sensor.vacuum_cleanups_today.attributes.value | replace("h ", "0") | replace("m", "") | int | default(0) > 10 }}'

but while this would be logically correct it mathematically wrong and hard to perceive to anyone reading so yours is much better and variations be reused in my other automations.

I recently started to avoid using numeric_state intentionally to avoid dealing with sensors values not always being numerical. More about that here.

Don’t listen to that. It’s very old, and given the way things work now (and I suspect the way they did even back then), there are wrong conclusions being drawn.

First and foremost, EVERY entity’s state is a string, even if it looks like a number. (And by “state” I mean as in a State Object’s state field.)

Second, the numeric_state trigger and condition first attempt to convert the entity’s state (string) to a float (i.e., number) before making the comparison to above and/or below. They also handle the case where the state is ‘unknown’ or the state object is None, etc.

In most cases, the numeric_state trigger or condition is the same as something like {{ states('XXX.YYY')|float > above|float }}.

So I polished automation to final state of the art.

- alias: Send Servantess to dock when someone arrives home during repeated cleanup
  trigger:
    - platform: state
      entity_id: group.people
      to: 'home'
  condition:
    - condition: state
      entity_id: vacuum.servantess
      state: 'cleaning'
    - condition: template
      value_template: >
        {% set s = state_attr('sensor.vacuum_cleanups_today', 'value') %}
        {% set t = strptime(s, '%Hh %Mm' if 'h' in s else '%Mm') %}
        {{ states('sensor.vacuum_cleanups_today') | int > 1 and (t.hour * 60 + t.minute) > 10 }}
  action:
    - service: vacuum.return_to_base
      entity_id: vacuum.servantess

I intentionally left cleaning state condition separated to return quickly before setting variable and performing calculations.

I like final result very much. Thank you all.

1 Like