Daily max on numeric sensor: how to. in core

oké so I cant believe I have to ask, but somehow I discovered I need this daily max sensor, and I dont know how to make it in HA core.

usecase: my solar inverter has a hard limit of 7500 W, and, since my panels can return more, it is the ultimate max I should see on a regular sunny day

if it doesnt, it means either dirt, or damage.

I can do the extrema setting on a custom mini-graph-card, but, other than that, I havent been able to find a solution in core HA

statistics or min/max, the combine helper entities dont do as I need (even the max entity goes down when next measurement is less than a previous max), and utility meter seems not made for the job either

maybe some nifty template ? not sure how in that case, need I combine that with a trigger, and how to reset for the next day..

I’ve been made aware of GitHub - jeroenterheerdt/HADailySensor: Sensor for Home Assistant that gets reset at midnight · GitHub, but again, I’d prefer a core solution

seems so simple: based on solar_panels_production, create a solar_panel_production_max

as said , this doesnt cut it.

this shows it in the right extrema

would this be ok:

template:

  - triggers:

           # midnight reset
      - trigger: time
        at: "00:00:00"

          # Update whenever solar power changes (actuele_opbrengst)
      - trigger: state
        entity_id: sensor.zp_actuele_opbrengst
    sensor:
      - name: Zp dagelijkse maximale opbrengst
        unique_id: zp_dagelijkse_maximale_opbrengst
        state: >
          {% set current = states('sensor.zp_actuele_opbrengst') | float(0) %}
          {% set previous = this.state | float(0) %}

          {% if trigger.platform == 'time' %}
            0
          {% else %}
            {{[current, previous] | max}}
          {% endif %}

or with trigger.id:

template:

  - triggers:

      - trigger: time
        at: "00:00:00"
        id: reset

      - trigger: state
        entity_id: sensor.zp_actuele_opbrengst
        id: update
    sensor:
      - name: Zp dagelijkse maximale opbrengst
        unique_id: zp_dagelijkse_maximale_opbrengst
        state: >
          {% set current = states('sensor.zp_actuele_opbrengst') | float(0) %}
          {% set previous = this.state | float(0) %}

          {% if trigger.id == 'reset' %}
            0
          {% else %}
            {{[current, previous] | max}}
          {% endif %}

add

        device_class: power
        state_class: measurement
        unit_of_measurement: W

and record the entity.

Have you looked at this?

Statistics - Home Assistant

Yes,thx I have that too

Not sure it works yet as I can’t see a history on it

Above the Template below the statistics

The Statistics integration only does rolling max values, there’s no way to limit it to the current day and still have a 24 hour max span.

No error checking but this would work.

template:
  - triggers:
      - trigger: state
        entity_id: sensor.zp_actuele_opbrengst
        id: update
    actions:
    - action: recorder.get_statistics
      data:
        statistic_ids:
        - sensor.zp_actuele_opbrengst
      period: day
      start_time: "{{ today_at().isoformat() }}"
      types:
      - max
      response_variable: statistics
    sensor:
      - name: Zp dagelijkse maximale opbrengst
        unique_id: zp_dagelijkse_maximale_opbrengst
        state: >
          {{ statistics['sensor.zp_actuele_opbrengst'][0]['max'] }}

Nice, never thought about that recorder option, thx.

Will let you know

I think you need another “statistics” in there…

state: >
  {{ statistics['statistics']['sensor.zp_actuele_opbrengst'][0]['max'] }}

maybe, I’ve never used the action before. Seems like a redundant word

This is the output

{% set statistics = {"statistics":{"sensor.zp_actuele_opbrengst":[{"start":"2026-07-26T22:00:00+00:00","end":"2026-07-27T22:00:00+00:00","max":7495.938}]}} %}

Or in yaml

statistics:
  sensor.zp_actuele_opbrengst:
    - start: "2026-07-26T22:00:00+00:00"
      end: "2026-07-27T22:00:00+00:00"
      max: 7495.938

Btw there was a tiny edit required in the action

action: recorder.get_statistics
data:
  statistic_ids:
    - sensor.zp_actuele_opbrengst
  period: day
  start_time: "{{ today_at().isoformat() }}"
  types:
    - max
response_variable: statistics

Yeah, for some reason it doesn’t follow the pattern of other responses. There are a few that don’t, and it’s really annoying because it makes merge_response() fail. Actually, it doesn’t fail… it just doesn’t give the desired effect.

Fear this is going to be a costly sensor…. Updating very often and talking to the db like that)…

How often do you need it to actually update? Since it’s going to get the max no matter when you run it, there’s no real need to run it on every state update… use a Time Pattern trigger to decrease the time resolution to what you need.

or a trigger when you want the information

Yep, already done :slight_smile:

Could do so yes, but I think I like to have it permanently in the dash.

But guess it doesn’t harm to eg set per hour and per button click

I changed that response_variable’s name to make clear how it works, and dont double that name ‘statistics’.
Ive put the 2 sensors in 1 trigger config. Hope that works, and the trigger reset wont mess with the recorder template.

added the (input_)button, also added a condition to limit evening updates..
not sure the button.pressed also triggers clicking the input_button.. I might just need the trigger: state to be sure

template:

  - triggers:

      - trigger: time
        at: "00:00:00"
        id: reset

      - trigger: time_pattern
        minutes: /15

      - trigger: button.pressed
        target:
          entity_id: input_button.trigger_zp_opbrengst

#       - trigger: state
#         entity_id: sensor.zp_actuele_opbrengst
#         id: update
    conditions:
      condition: state
      entity_id: sun.sun
      state: above_horizon
    actions:
      action: recorder.get_statistics
      data:
        statistic_ids:
          - sensor.zp_actuele_opbrengst
        period: day
        start_time: "{{ today_at().isoformat() }}"
        types:
          - max
      response_variable: opbrengst_statistics

    sensor:
      - unique_id: zp_dagelijkse_maximale_opbrengst
        name: Zp dagelijkse maximale opbrengst
        state: >
          {% set current = states('sensor.zp_actuele_opbrengst') | float(0) %}
          {% set previous = this.state | float(0) %}

          {% if trigger.id == 'reset' %}
            0
          {% else %}
            {{[current, previous] | max}}
          {% endif %}
        <<: &power
          device_class: power
          state_class: measurement
          unit_of_measurement: W

      - unique_id: zp_dagelijkse_maximale_opbrengst_statistics
        name: Zp dagelijkse maximale opbrengst statistics
        state: >
          {{ opbrengst_statistics['statistics']['sensor.zp_actuele_opbrengst'][0]['max'] }}
        <<: *power

Far from an expert & might have missed something obvious, but I doubt it’ll ever trigger. Unless the sun is still up at midnight, your condition will block it.

yes…
thats an issue of course, as the midnight trigger should reset the template…
ok thanks for obvious I missed.

well there was the next morning.

from top to bottom:

  • helper Combined Max: resets and updates continuously, however, Ive also seen it decrease, so apparently not always Max of the day

  • trigger template as per post above, doesnt reset and still showing last days max.

  • trigger template recorder.get_statistics: resets and trigger per 15 min now

  • helper Statistics: doesnt reset and shows yesterdays max

so this probably shows the most robust and efficient solution without having to create a lot of ‘guards’ in triggers or otherwise is the the trigger template using the recorder.get_statistics.

it doesnt need a ‘reset’ trigger, as it does that by design. the update frequency is full controllable.

I do want to add a trigger.to_state > trigger.from_state, but tht is less than obvious as I am not sure the trigger is now bound to the output directly…

could anyone of the responders here please think with me on that matter?

update

the trigger button doesnt work, so I changed to the state: version for that input_button. and added a attributes for last_triggered to be sure it updated.

and what shows: the state doesn’t update, while the attribute does. so my guess now is that the record db isnt updated yes? could that be?

will have to resort to the regular template with nightly reset I guess (the top one in the list here), it updates immediately upon request. the recorder statistics template still lags even though pushed manually

ok, so I deleted the statistics and min/max to leave the regular trigger template, and trigger template with action recorder.get_statistics.

Ive adapted the triggers a bit more, gave all a trigger.id to play with in conditions, and am seeing good behavior now.

correct resetting at midnight, and steady state changes during the day

to be sure it only updates on increase I added a condition template. At first I feared that would only be useful for the action block, and not for the regular sensor, but the documentation clearly states

Define conditions that have to be met after a trigger fires and before any actions are executed or sensor updates are performed (for trigger-based entities only)

so that is great. Otherwise I would have had to take these 2 out of the 1 trigger config…

(also, it seems to not harm that the recorder.get_statistics sensor is triggered by the reset trigger. its own configuration does as much itself, works fine as far as I can see)

the only difference between the 2 now is that the state sensor is more accurate during increase than the statistics version, simply because the DB isnt updated as often and it lags a it because of that.

you can clearly see that in the attributes, where the last triggered shows they are updated at the same time, while state is behind

final config for now:

yaml for state based and recorder based trigger templates
##########################################################################################
# Templates
# https://www.home-assistant.io/integrations/template/
##########################################################################################

template:

  - triggers:

      - trigger: time
        at: "00:00:00"
        id: reset

#       - trigger: time_pattern # <-- not used, replaced by to_state > from_state condition
#         minutes: /15

      - trigger: state
        entity_id: input_button.trigger_zp_opbrengst
        id: trigger_manual

      - trigger: state
        entity_id: input_button.trigger_reset
        id: reset_manual

      - trigger: state
        entity_id: sensor.zp_actuele_opbrengst
        id: update
    conditions:
    # see: https://www.home-assistant.io/integrations/template/#conditions
    # also needs to be met before sensor updates are performed
    # so not only for the actions block
      - >
        {{trigger.to_state.state > trigger.from_state.state or
          trigger.id in ['reset','reset_manual','trigger_manual']}}
#       - condition: state # <--- not useful as template condition will capture all
#         entity_id: sun.sun
#         state: above_horizon
    actions:
      action: recorder.get_statistics
      data:
        statistic_ids:
          - sensor.zp_actuele_opbrengst
        period: day
        start_time: "{{ today_at().isoformat() }}"
        types:
          - max
      response_variable: opbrengst_statistics

    sensor:
      - unique_id: zp_dagelijkse_maximale_opbrengst
#         name: Zp dagelijkse maximale opbrengst
        state: >
          {% set current = states('sensor.zp_actuele_opbrengst') | float(0) %}
          {% set previous = this.state | float(0) %}

          {% if trigger.id == 'reset' or trigger.id == 'reset_manual' %}
            0
          {% else %}
            {{[current, previous] | max}}
          {% endif %}
        <<: &power
          device_class: power
          state_class: measurement
          unit_of_measurement: W
        attributes:
          last_changed: >
            {{(this.last_changed|as_local).strftime('%X - %x')}}
          last_trigger: >
            {{trigger.id}}