Heads up! Upcoming breaking change in the Template integration

Tags: #<Tag:0x00007f7397e2a830>

True, but an int can remedy that whereas converting UTC to local time is messy.
Misunderstood your point about the microseconds.

All this to say that states.sensor.time.lastupdated is more like using utcnow() than now(). The user needs to keep that in mind if they intend to use it in their date calculations.

And as long as they realise it’s got a one minute resolution, no good for 30 second comparisons for example.

1 Like

I think the takeaway is that states.sensor.time.last_updated can be useful but should be used judiciously because it isn’t the equivalent of either now() or utcnow(). As you mentioned, it’s sensor.time after all so it only has 1-minute resolution and last_updated is UTC-based.

That’s also good advice for existing Template Sensors that use this:

    entity_id: sensor.time

and then proceed to use now() to perform time arithmetic that look for sub 1 minute differences. The darn thing is only going to be evaluated every minute so looking for, as you mentioned, a 30-second difference would be a mistake. You’re obliged to look for differences in whole minutes.

Hopefully, very few people have made that mistake.

1 Like

I counted up my template sensors and binary template sensors. 19 uses of sensor.time and 5 uses of sensor.date.

Not too onerous a change.

In fact I think I could replace the majority of them with the custom scheduler card and significantly less input booleans. Might have to have a bit of an experiment with it at some stage.

Would you mind sharing a link?

Sure, be aware it is still very much in development though:

Do we have a good way to convert last_updated to local time? We could expose as_local to jinja if there isn’t:

1 Like

I’m with Bartem… This is getting pretty hard to follow.
But it is concerning me.

I have (quite a few) templates like this.

      irrigation_cycle1_duration_in_seconds:
        friendly_name: Cycle 1 Duration In Seconds
        entity_id:
          - input_number.irrigation_number_of_zones
          - sensor.irrigation_cycle1_zone1_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone2_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone3_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone4_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone5_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone6_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone7_actual_duration_in_seconds
          - sensor.irrigation_cycle1_zone8_actual_duration_in_seconds
        value_template: >
          {% set ns = namespace(duration = 0) %}
          {% for zone in states.sensor if zone.object_id.startswith('irrigation_cycle1_zone') and 
                                          zone.object_id.endswith('_actual_duration_in_seconds') %}
            {% if loop.index <= states('input_number.irrigation_number_of_zones') | int %}
              {% set ns.duration = ns.duration + (zone.state | int) %}
            {% endif %}
          {% endfor %}
          {{ ns.duration }}

Unless I am mistaken (quite possible of course) this won’t work?

I think you just wipe out all the entity_id section. Done.

Yeah but how will it know what to watch as the entity names are not explicitly stated, only what they startwith and endwith?

But if that works then fantastic!

1 Like

This is exactly what I’m confused about (even sober!). The answer appears to be to shoehorn the entity_id into the template, but I can’t see how that’s better than the current approach, and would actually go so far as to say that it’s a lot less user friendly :man_shrugging:

It will work. (Oh if it doesn’t you can add a dummy time template… that has been discussed above) However from what I have read it will parse the template and know what to do…

Worse case is that you move the sensors in the template. When I get on a pc I can rejigger it for ya.

1 Like

how can we test this locally?

are you saying the new system will also understand this:

      time_until_next_alarm:
        friendly_name: Time until next alarm
        entity_id:
          - sensor.time
          - sensor.next_alarm_timestamp
          - input_boolean.alarmclock_wd_enabled
          - input_boolean.alarmclock_we_enabled
          - input_number.alarmclock_wd_hour
          - input_number.alarmclock_wd_minute
          - input_number.alarmclock_we_hour
          - input_number.alarmclock_we_minute
        value_template: >
          {% set timestamp = states('sensor.next_alarm_timestamp')|int %}
          {% if timestamp %}
            {% set delta = timestamp-now().replace(second=0).replace(microsecond=0).timestamp() %}
            {% if delta/3600 < 1 %} {{delta|timestamp_custom('%-M',False)}}
            {% elif delta/86400 <1 %} {{delta|timestamp_custom('%-H:%-M',False)}}
            {% else %} {{(delta// 86400)|int}}:{{delta|timestamp_custom('%-H:%-M',False)}}
            {% endif %}
          {% else %}
            Not set, relax
          {% endif %}

where all of the above listed entity_id’s which influence sensor.next_alarm_timestamp are found without explicitly listing them anymore?

      irrigation_cycle1_duration_in_seconds:
        friendly_name: Cycle 1 Duration In Seconds
        value_template: >
          {% set zones = [
            states.sensor.irrigation_cycle1_zone1_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone2_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone3_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone4_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone5_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone6_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone7_actual_duration_in_seconds,
            states.sensor.irrigation_cycle1_zone8_actual_duration_in_seconds,
            ] %}
          {% set threshold = states('input_number.irrigation_number_of_zones') | int %}
          {{ zones | map(attribute='state') | map('int') | select('<', threshold) | list | sum }}

Might want to test it in the template tester… haven’t verified that it’s correct.

You shouldn’t have needed those for that because the template is only based on states(‘sensor.next_alarm_timestamp’). As long as states(‘sensor.next_alarm_timestamp’) updates properly, then you should be good.

yes, Thats what we thought at the time. However, time has taught updating of these sensors was rather peculiar, and had me add entity_id’s consecutively. Oddly enough, the sensor.next_alarm_timestamp itself already needs these same entity_id’s listed, even while these are in the template itself, except the sensor.time.

this has been the case since sometime ago, I could give it (taking out the entity_id’s) another try in the current HA.

of course, the timestamp sensor is quite the one:

  - platform: template
    sensors:
      next_alarm_timestamp:
        friendly_name: Next alarm timestamp
        entity_id:
          - sensor.time
          - input_boolean.alarmclock_wd_enabled
          - input_boolean.alarmclock_we_enabled
          - input_number.alarmclock_wd_hour
          - input_number.alarmclock_wd_minute
          - input_number.alarmclock_we_hour
          - input_number.alarmclock_we_minute
        value_template: >
          {% macro getalarm(offsetday=0) %}
          {% set day = (now().weekday() + offsetday) % 7 %}
          {% set offset = offsetday * 86400 %}
          {% set fmat = 'd' if day in range(5) else 'e' %}
          {% set hour = 'input_number.alarmclock_w{}_hour'.format(fmat) %}
          {% set minute = 'input_number.alarmclock_w{}_minute'.format(fmat) %}
          {% set alarm = states(hour)|float|multiply(3600) + states(minute)| float|multiply(60) %}
          {% set time = now().hour * 3600 + now().minute * 60 %}
          {{(offset - time) + alarm if offsetday else alarm - time}}
          {% endmacro %}

          {% set weekday = is_state('input_boolean.alarmclock_wd_enabled','on') %}
          {% set weekend = is_state('input_boolean.alarmclock_we_enabled','on') %}

          {% set weekdays = [0,1,2,3,4] %}
          {% set weekends = [5,6] %}
          {% set day = now().weekday() %}
          {% set nextday = day + 1 if day != 6 else 0 %}
          {% if nextday in weekdays %}
            {% if weekday %}
              {% set offsetday = nextday %}
            {% elif weekend %}
              {% set offsetday = weekends[0] %}
            {% else %}
              {% set offsetday = None %}
            {% endif %}
          {% elif nextday in weekends %}
            {% if weekend %}
              {% set offsetday = nextday %}
            {% elif weekday %}
              {% set offsetday = weekdays[0] %}
            {% else %}
              {% set offsetday = None %}
            {% endif %}
          {% else %}
            {% set offsetday = None %}
          {% endif %}

          {% if offsetday != None %}
            {% set offset = offsetday-day if offsetday > day else offsetday - day + 7 %}
            {% if (day in weekdays and weekday) or (day in weekends and weekend) %}
              {% set today_alarm = getalarm()|float %}
            {% else %}
              {% set today_alarm = -1 %}
            {% endif %}
            {% set next_alarm = getalarm(offset)|float %}
            {% set time = now().replace(second=0).replace(microsecond=0) %}
            {% if today_alarm > 0 %}
              {{as_timestamp(time) + today_alarm}}
            {% else %}
              {{as_timestamp(time) + next_alarm}}
            {% endif %}
          {% else %}
            0
          {% endif %}

:wink:

Yes, this will need to be re-tailored.

          {% macro getalarm(offsetday=0) %}
          {% set day = (now().weekday() + offsetday) % 7 %}
          {% set offset = offsetday * 86400 %}
          {% if day in range(5) %}
            {% set hour = states('input_number.alarmclock_wd_hour') %}
            {% set minute = states('input_number.alarmclock_wd_minute') %}
          {% else %}
            {% set hour = states('input_number.alarmclock_we_hour') %}
            {% set minute = states('input_number.alarmclock_we_minute') %}
          {% endif %}
          {% set alarm = hour|float|multiply(3600) + minute|float|multiply(60) %}
          {% set time = now().hour * 3600 + now().minute * 60 %}
          {{(offset - time) + alarm if offsetday else alarm - time}}
          {% endmacro %}

@bdraco Do you need to have the entity_id near states, state in templates? Or do we find valid entity_id’s that are concatenated in the code?

I.E. a formatted string that builds an entity_id, a concatenated string that builds an entity_id, etc…

jut did a quick test, and commented all entity_ids of all sensors, except for using

          - sensor.time
          - sensor.next_alarm_timestamp

on them, which works out fine for now.

the big one posted above now only has sensor.time, and seems to fare well at it… Big changes must have happened behind the screens for this to be possible now. Which is cool indeed.