Help needed on templates

I use UI to build 2 sensors via the helper / template (Settings => Devices & services => Helpers):

  • 1st one being a statistics sensor to grab max value (ie value_max) over last 90 minutes or last 50 samples of my temperature sensor.
  • 2nd one is also a statistics sensor to grab min value over same period of the same entity

(I can share screenshots if needed.)

And then in my configuration.yaml, I also have this, also a template, to compare the max and min.

template:
  - sensor:
      - name: "ecobee Homekit Max-Min Temp Difference over 1.5hr"
        unique_id: ecobee_homekit_max_min_temp_difference_over_1_5hr
        state: >
          {{ states('sensor.ecobee_homekit_max_temp') | float(0) - 
             states('sensor.ecobee_homekit_min_temp') | float(0) }}

The idea is that if the max and min being the same for long enough, I'm going to run an automation to restart my HomeKit. The setup has been working for months.

But my question being, is there a way to consolidate the 3 of those template sensors into one?
I think it is possible, but not quite sure where to start. Hence the question. Figured it could be a good opportunity to improve my jinja2-template-fu.

Are you just using this whole collection as a proxy for the Homekit/sensor failing to update or, are you using the min and max sensors elsewhere?

Correct. As a proxy. No other use for the min and max temperature sensors.

Then they are unnecessary, you can just use the last_reported property.

template:
  - binary_sensor:
      - name: "ecobee Homekit no report for 1.5hr"
        unique_id: ecobee_homekit_no_report_1_5hr
        state: >
          {{ now() >= states.sensor.YOUR_ECOBEE_TEMP.last_reported + timedelta(minutes=90) }}
1 Like

Good point.
And just to double check, this would then become a binary sensor, because the outcome of the state would become true or false, correct?

Good catch. I should have set it up as a binary sensor, I have made the change in my previous post.

This is secondary at this point, since we are tracking last_reported thanks @Didgeridrew. However, still, as a practice for templates, is there a good way to merge these 3 sensors into one...?

sensor:
  - platform: statistics
    name: "ecobee Homekit Max Temp"
    entity_id: sensor.ecobee_homekit_max_temp
    state_characteristic: value_max
    max_age:
      minutes: 90
    sampling_size: 50
    precision: 2
  - platform: statistics
    name: "ecobee Homekit Min Temp"
    entity_id: sensor.ecobee_homekit_min_temp
    state_characteristic: value_min
    max_age:
      minutes: 90
    sampling_size: 50
    precision: 2
template:
  - sensor:
      - name: "ecobee Homekit Max-Min Temp Difference over 1.5hr"
        unique_id: ecobee_homekit_max_min_temp_difference_over_1_5hr
        state: >
          {{ states('sensor.ecobee_homekit_max_temp') | float(0) - 
             states('sensor.ecobee_homekit_min_temp') | float(0) }}

It's fine if no good way to optimize this. I'm trying to learn here.

I think that's the most reasonable way to do it.

I'm pretty sure it could also be done with a single trigger-based template sensor with an actions block. You could use the sql.query action to essentially do the same thing as the Statistics sensors are doing...

EDIT:

It would be something like the following, but SQL isn't my forte, so additional tweaks may be necessary:

template:
  - triggers:
      - trigger: time_pattern
        minutes: /3
    variables:
      max_entity: sensor.ecobee_homekit_max_temp
      min_entity: sensor.ecobee_homekit_min_temp
      max_age_minutes: 90
      sampling_size: 50
      age_phrase: "{{ '-'~max_age_minutes~' minutes'}}"
    actions:
      - action: sql.query
        data:
          query: |
            SELECT
              states.state
            FROM
              states
              INNER JOIN states_meta ON
                states.metadata_id = states_meta.metadata_id
            WHERE
              states_meta.entity_id = '{{max_entity}}'
              AND last_updated_ts <= strftime('%s', 'now', '{{age_phrase}}')
              AND state != 'unavailable'
            ORDER BY 
              last_updated_ts DESC
            LIMIT '{{sampling_size}}'
        response_variable: maxes
      - action: sql.query
        data:
          query: |
            SELECT
              states.state
            FROM
              states
              INNER JOIN states_meta ON
                states.metadata_id = states_meta.metadata_id
            WHERE
              states_meta.entity_id = '{{min_entity}}'
              AND last_updated_ts <= strftime('%s', 'now', '{{age_phrase}}')
              AND state != 'unavailable'
            ORDER BY 
              last_updated_ts DESC
            LIMIT '{{sampling_size}}'
        response_variable: mins
    sensor:
      - name: Ecobee Min/Max diff
        state: |
          {% set maximus = maxes.result | map(attribute='state') | select('is_number') | map('float') | max %}
          {% set minimus = mins.result | map(attribute='state') | select('is_number') | map('float') | min %}
          {{ (maximus - minimus)|round(2) }}

EDIT:

  • 2026-05-28: The sql.query action is, apparently, only available if you have at least one SQL sensor already configured. If you do not currently have a need for a SQL sensor, the following is the simplest query to initiate the integration:
SELECT states.state
FROM
  states
LIMIT
  1
Screenshot of query in the SQL integration's UI config flow