Heads up! Upcoming breaking change in the Template integration

OK, just to define the situation clearly, a very common Template Sensor application is to report the number of lights that are currently on.

The neatest way to do it would be to define a group containing all the lights we want to count and then use this simple Template Sensor.

      all_lights:
        friendly_name: 'Lights'
        value_template: >-
          {{ expand('group.all_lights') 
             | selectattr('state', 'eq', 'on')
             | list | count }}

That’s not achievable now but will be given the new way expand() is handled. However, the extra convenience would be this scenario:

  • I add another light to group.all_lights and sensor.all_lights is immediately aware of its presence and monitors its state.

The upshot is that adding/subtracting entities from groups immediately updates the behavior of any Template Sensors that use those groups. That would be tremendously convenient.

Ideally, it would happen transparently, immediately after the group’s membership is modified. However, it wouldn’t be a burden if we first have to run reload services for groups and templates.

I’d be worried that scan_interval might result it the setting being added unnecessarily since its only needed for cases were we want to refresh based on time. This could be the setup for a bad day with and overloaded instance if it gets added to all of the a user’s template entities.

Maybe a force_refresh_interval ?

Yeah, I was just reusing an existing property, but a new one would work too. As long as we have something to catch those fringe cases.

After looking at it a bit more, I think scan_interval would be ok as we only have scan_interval per platform and this would be per entity.

1 Like

Hmm. I think I’d still prefer to add sensor.date using the workarounds above, so that I know my daily sensors update at midnight. A scan_interval of a day has no guarantee of when during the day it would occur.

Would work for minute or hourly updates though, unless they had to occur on the hour for some reason.

just reading this topic, do mine sensors also needs to be changed? i have an entitiy in the value template…
ex:

      vacuum_fan_speed:
        friendly_name: Fan Speed
        entity_id:
          - vacuum.roborock
        value_template: 'Mode: {{ states.vacuum.roborock.attributes.fan_speed }}'
        icon_template: 'mdi:speedometer'

Adding an entity into the template that will have a state change when you want it is definitely the most efficient/best option and should be relatively future proof. Additional options like scan_interval and the deprecated entity_id are going to cover some subset of use cases, and we don’t want to end up with a buffet of options that are difficult to maintain and conflict with each other.

1 Like

This. Exactly.

I can count on sensor.date to change state at the very beginning of each day. That’s often very important for date-based calculations.

An interval of 86400 seconds is the length of a day but the end of that interval is likely to depend on when the countdown started … typically meaning from the moment Home Assistant was restarted.

1 Like

You would just have to delete this:

        entity_id:
          - vacuum.roborock

From your sensor.

Also it would be prudent to update your templates to this format to prevent errors when the entity is initialising:

value_template: "Mode: {{ state_attr('vacuum.roborock', 'fan_speed') }}"

Though this is a general recommendation unrelated to this change.

3 Likes

I’m not sure I understand.
@petro, @123, @tom_l, @bdraco (I think this applies to all)

I Agree with Teras that sensor.date is the best way to update an evaluation for a new day even if we need to {% enclose %} it, to use it.
Petro wants now() the be evaluated on a minute basis (any other is unnecessary, surely ??? ) But why use now() ? It’s mainly used when you neep a timestamp somewhere in an evaluation.
You can use sensor.time to get a timestamp (I have done this myself) but it is rather long winded.
Rather than force refreshes, or automate an update or {% include %} an evaluation why not just create a new short-cut to generate a timestamp from sensor.time.? (which then will be included given the PR as listed above)
What have I missed ?

I think what Muttley is asking (and if not, I am…) is…

Is there a nice simple explanation of this change for those of us with not enough blood in our alcohol streams on this fine summer evening?

5 Likes

now() is a datetime object so it has methods to get year, month, day, timestamp, etc.

now().timestamp()

In contrast, sensor.time reports the current time as a string. Period.

Upcoming changes to the Template integration:

  • Many of the enhancements will make things easier but you’re not obligated to use them; your existing Template Sensors can remain unchanged.
  • This post focuses on the one thing that may oblige you to modify some of your existing Template Sensors.

… and cheers! :wine_glass:

So ‘no’ then

:stuck_out_tongue_winking_eye:

Back at ya, buddy :beers:

2 Likes

Yes …

… But

That’s what I’m asking -
From now() we can get

  1. a timestamp 2020-09-01 22:42:47.175718+01:00 - (get this from sensor.time instead, new shortcut)
  2. Year - well I can get this from sensor.date {{ states(‘sensor.date’) [0:4] }}
  3. month - well I can get this from sensor.date {{ states(‘sensor.date’) [5:7] }}
  4. date - well I can get this from sensor.date {{ states(‘sensor.date’) [8:10] }}
  5. day - well we’d need a new shortcut for this as well from sensor.date

Getting timestamp from sensor.time the hardway : -

{{ as_timestamp(states('sensor.date') ~ ' ' ~ states('sensor.time')) }}

Edit: 
Duh !
{{ as_timestamp(states('sensor.date_time')) }} {# eh, this doesn't work :confused: #}
{{ as_timestamp(states('sensor.date_time').replace(',','')) }} {# works but feels cludgy #}

Admittedly you get no seconds but as you are only interested in minute updates (by sensor.time) then that’s not a problem.
When you are evaluating a condition there should be no prohibition on using now() and that would evaluate correctly

1 Like

I donno what you’re going on about, I was just talking about the parser finding now() and creating a listener that acts on the solid minute change. The template would look simple, but you wouldn’t need sensor.time.

{{ now().minute }}
{{ now().hour }}
{{ now().year }}
etc

All these contain now(), so the template would be updated on the minute every minute. My comments were specifically to counteract the changes that break this template:

Yes, I got that but there seems a lot of resistance to setting a listener on now()
and you don’t need one, given the above (other than for day where you’d have to create the timestamp and ask for the day)

And I also get you are trying to avoid a breaking change

If we did something with now() we would have to choose between a time pattern and an interval. It is possible to implement something like now(1) to force an update every second or now(60) to force an update every 60 seconds. I did a rough implementation of this, but discarded the idea because doesn’t cover the case where the user only wants updates for a specific time pattern like hours = '*', minutes = '0', seconds='0' so they may get a time second that is updating when they don’t want it.

The next thought was to implement something like now(hours=*, minutes=0, seconds=0) so you could get now(hours=*, minutes=*, seconds=*) which would cover all the use cases … except if you have multiple now() since we won’t know which one to use, or what the default should be. In this case we could setup multiple trackers but this makes everything very complex and the user may not be expecting all the updates it generates which gets us back to the performance concerns.

{% if states.light.a.state == "on" %}{{ utcnow(hours=*, minutes=0, seconds=0) }}{% else %}{{ utcnow(hours=/2, minutes=0, seconds=0) }}{% endif %}

One potential alternative would be to make a quick way to create time interval sensors. Then you can put them anywhere in the template and we wouldn’t have to explain how they worked.

1 Like

So most of this conversation is over my head as far as the workings of it all… (I commend you gents on your amazing brains…) but in checking through my templates I found a few of these and I’m kind of lost as to if they would be affected or if I could just strip out the entity_id…

    display_night_cards_armed:
      friendly_name: Display card between 9PM and 5AM and Alarm Armed
      entity_id: sensor.time
      value_template: >
          {% set ct = now().hour + now().minute/60 + now().second/3600 %}
          {{ (21 <= ct or ct <= 5) and is_state('alarm_control_panel.ha_alarm','armed_home')}}

I see discussion about use of “now( )” but wasn’t sure if that is something that will work… or was being discussed if it should work…