Heads up! Upcoming breaking change in the Template integration

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.

As long as the state is accessed it doesnā€™t matter how the string is built now as it will still find it. We donā€™t do analysis on the template string anymore, the analysis is done by looking at which states we touch during rendering of the template.

2 Likes

Excellent, thatā€™s good to know. @Mariusthvdb, @klogg shouldnā€™t need to use the reworked templates.

[ā€¦stops typing mid post to say ā€˜thank youā€™ your reworked template seemed to work with initial tests.]

So, just to be completely clearā€¦
You are now saying my original templates will work untouched, after I remove the entity_id?
(albeit that I might prefer yours anyway :wink: )

EDIT: If that is so then it seems the whole discussion does ā€˜simplyā€™ come down to one of when in time sensors are evaluated and how to control that?