How to count the times an automation was triggered, restart persistent

well my cup of coffee sank in…

Counter Counter - Home Assistant

which narrows down the issue to ‘recording’ the moments only. I will include it in recorder ofc. and might not even require the below trigger template sensor because of that

template:

  - trigger:

      - platform: state
        entity_id: counter.reload_tradfri_integration

    sensor:

      - unique_id: count_number_automation_reloaded
        name: Count number automation reloaded
        icon: mdi:motion-sensor
        state: >
          {{states('counter.reload_tradfri_integration')}}
        attributes:
          last: >
            {% set stamp = as_local(states[trigger.entity_id].last_changed).strftime('%X') %}
            {{ stamp }}
          <<: &history
            history_1: >
              {{this.attributes.last|default('Not yet set')}}
            history_2: >
              {{this.attributes.history_1|default('Not yet set')}}
            history_3: >
              {{this.attributes.history_2|default('Not yet set')}}
            history_4: >
              {{this.attributes.history_3|default('Not yet set')}}
            history_5: >
              {{this.attributes.history_4|default('Not yet set')}}
            last_triggered: >
              {{trigger.to_state.last_changed.isoformat()}}

and just add the service to the automation I already mentioned above…
o dear, never mind me.

played a bit with the format of the attibrutes, considering it has to cross days, so adjusted to '%D %X'

w/o counter

{% set previous = this.state | int(0) if this.state is defined else 0 %}
{{ previous + 1 }}
1 Like

thanks Petro,
that was what I was looking for in the first place indeed!

I kept looking at the condition I have in that same automation and tried to rewrite that to a state template…

    condition:
      - >
        {{now() - this.attributes.last_triggered > timedelta(minutes=5) if
          this.attributes.last_triggered is not none else true}}

you managed…
moved the solution checkmark to your post

I’m sure there’s other ways to do this and the template might be a bit verbose, but it should work. You might want to add a trigger to reset it to zero though.

If you are interested, there’s a way to create a single “history” attribute containing a list of historical values (as opposed to multiple attributes, each containing one historical value).

Another user needed to maintain a record of two things: the names of the last 5 songs that had been played and the time when each song was played. What I suggested creates a list of the desired information sorted in reverse chronological order (newest is first).

For your application it would be simpler because you only need to track one item, the time. The advantage of the suggested approach is that the history can be easily extended beyond 5 entries.

Here’s another example of the same principle used to record when a door was opened, closed, and the duration it was left open.

Not shown in these examples, but something that I have implemented for my own application, is the ability to purge the history via a custom event. The second example comes close to demonstrating how to do that because it uses a Time Trigger (instead of an Event Trigger) to purge history every day at midnight.


On a separate note, I believe the following template can also be used as an incrementing counter. Please note that I only tested it in the Template Editor with a non-existent entity, like states.counter.xyz.state, and not in a Trigger-based Template Sensor with this.state, but I think it should work.

{{ this.state | default(0) | int(0) + 1 }}
1 Like

Thanks Taras,

that is most attractive indeed!
I confess having something similar for some of my motion template triggers (made with guidance by @TheFes ):

      - unique_id: last_time_motion_triggered
        name: Last time motion triggered
        icon: mdi:update
        state: >
          {{as_local(states[trigger.entity_id].last_changed).strftime('%X')}}
        attributes:
          history: >
            {%- set previous = this.attributes.history | default([]) %}
            {%- set new = [this.attributes.last_area | default('nowhere') + ': ' + this.state] %}
            {{ previous[-4:] + new }}
          last_area: >
            {{area_name(trigger.entity_id)}}

and did not consider that here.
Your 2 examples have even gone a bit further. I’ll copy those to my cookbook for sure. I believe they go a bit beyond the purpose if my current quest, but they are very useful in several scenarios I do use.

On the Markdown card: check this btw for some formatting I ran in today, it should be registered in the Frontend examples too.

Nice, and thanks again!

I already had vingerha’s post bookmarked. :slightly_smiling_face: It’s the alternating row shading that had caught my eye. I don’t use card-mod (my Dashboard is plain vanilla) but thought that was a neat application for the Markdown card (which I do use).

I made your suggestions into a test trigger template sensor, but it is not correct just yet:

      - unique_id: count_reload_tradfri_integration_single_attribute
        name: Count reload Tradfri integration single attribute
        icon: mdi:eye-plus-outline
        state: >
          {{this.state|default(0)|int(0)+ 1}}
        attributes:
          history: >
            {% set current = this.attributes.get('history', []) %}
            {% set new = [{
                "time": trigger.to_state.last_changed.isoformat() }] %}
            {{ (new + current)[:5] }}

on each trigger, it registers twice… both state and attributes. Ive reloaded the automation 2 times only (note it also shows the time to be identical 4 times):

lastly, note the difference in representation of the last_changed.isoformat() in both of my screens. When used in the single attribute, it shows as the timestamp, but when used as single attribute, it renders a nicely formatted time March 16, 2023 at 1:52:46 PM

What’s the trigger?

template:

  - trigger:

      - platform: state
        entity_id: automation.reload_tradfri_integration #counter.

I triggered it manually btw, but that should not make a difference I suppose. Could it be this triggers twice because of the automation itself? some attribute next to the state_changed maybe…

fwiw, this is the automation:

  - alias: Reload Tradfri integration
    id: reload_tradfri_integration
    mode: single
    trigger:
      - platform: event
        event_type: system_log_event
        event_data:
          level: WARNING
        id: observation
      - platform: event
        event_type: system_log_event
        event_data:
          level: ERROR
        id: failed
    condition:
      - >
        {{now() - this.attributes.last_triggered > timedelta(minutes=5) if
          this.attributes.last_triggered is not none else true}}
#       or:
#         - >
#           {{trigger.event.data.name == 'tradfri.base_class' and 'Observation failed for'
#             in trigger.event.data.message[0]}}
      - >
        {{trigger.event.data.name == 'homeassistant.components.tradfri' and
          'Keep-alive failed' in trigger.event.data.message[0]}}
#         - >
#           {{trigger.event.data.name == 'coap' and
#               'Error received in UDP connection under DTLS' in trigger.event.data.message[0]}}
    action:
      - service: counter.increment
        entity_id: counter.reload_tradfri_integration
      - service: system_log.write
        data:
          message: >
            Ikea Tradfri: '{{trigger.id}}' on {{trigger.event.data.name}} issue logged:
            {{trigger.event.data.message[0]}}.
            Reloading integration
          level: critical
          logger: homeassistant.components.tradfri
      - service: script.reload_tradfri_integration
      - service: system_log.write
        data:
          message: >
            Ikea Tradfri integration was reloaded because of {{trigger.id}}
            Full data:
            {{trigger.event}}
          level: critical
          logger: homeassistant.components.tradfri
      - service: persistent_notification.create
        data:
          title: >
            {{trigger.event.data.timestamp|timestamp_custom('%X')}}: Tradfri reloaded
          message: >
            {{now().timestamp()|timestamp_custom('%X')}}: Ikea Tradfri integration was
              reloaded because of {{trigger.id}}:
              Message: {{trigger.event.data.message[0]}},
              Logger: {{trigger.event.data.name}},
              Source: {{trigger.event.data.source}},
              Level: {{trigger.event.data.level}}

Redacted; nope. What I had in mind wouldn’t work.
What exactly do you want to detect with that State Trigger? In its current form it triggers when you enable/disable the automation and if any of its attributes changes value.

last_triggered
current

I suggest you limit the State Trigger to listen to changes to last_triggered.

      - platform: state
        entity_id: automation.reload_tradfri_integration #counter.
        attribute: last_triggered

I want the trigger sensor to count the number of times the automation gets triggered, or, more specifically, when the automation fires the action block (so its last_triggered is changed)

when I added the counter increment as service, that of course was no longer required, as the automation simply causes the secondary counter entity to increment.

I could also may fire a custom event as service:

- event: used_for_trigger_sensor_counter

and then use that event as trigger in the trigger template sensor:

template:

  - trigger:

      - platform: event
        event_type: used_for_trigger_sensor_counter

however, it would seem to me that I should be able to use the automations last_trigger directly…?

that does seem to help, it only registers a single increment now. so that is good. the attribute itself though still is not touched by this ofc, and lists 5 identical timings:

and I now see that the last_changed there is exactly what it returns. … the last changed of the automation (on/off) and not it’s last_triggered, or the last_changed of the ‘this.state’ for that matter.

this helps:

        attributes:
          history: >
            {% set current = this.attributes.get('history', []) %}
            {% set new = [{
                "time": trigger.to_state.attributes.last_triggered.strftime('%D %X') }] %}
            {{ (new + current)[:5] }}

restyled it a bit, and working nicely!

      - unique_id: count_reload_tradfri_integration_single_attribute
        name: Count reload Tradfri integration single attribute
        icon: mdi:eye-plus-outline
        state: >
          {{this.state|default(0)|int(0)+ 1}}
        attributes:
          herlaad_overzicht: >
            {% set triggered = trigger.to_state.attributes.last_triggered %}
            {% set op = as_local(triggered).strftime('%D %X') %}
            {% set current = this.attributes.get('history', []) %}
            {% set new = [{ 'op': op }] %}
            {{ (new + current)[:5] }}
          last_triggered: >
            {{trigger.to_state.attributes.last_triggered}}

FWIW, you’re recording just the time so there’s no need to store it as a key-value pair. In other words, the attribute’s value can simply be a list of strings (where each string is a time value) and not a list of dictionaries (where each dictionary contains a single key-value pair).

{% set new = [op] %}

The reason why the other examples used a list of dictionaries is because they tracked several things for each event (door opened time, door closed time, duration left open).


On a separate note, while experimenting with this technique, I encountered a Home Assistant feature that made the testing more challenging.

Let’s say you did some testing and the attribute now contains several items. However, you now want to significantly change how the attribute records information so you need to purge all of the recorded items.

So you delete the entity’s configuration and reload Template entities. The entity is now gone.

Now you recreate the entity’s configuration, same entity_id but with modifications to how the attribute is computed, and reload Template entities. The entity is created with the previous entity’s attribute data.

Home Assistant restores the data for Trigger-based Template Sensors. If you delete one, it doesn’t purge the delayed sensor’s stored data. So if you create a new Trigger-based Template Sensor with the same entity_id, it gets the previous one’s stored data.

people wanted restored states :man_shrugging:

I assumed that if you create a Trigger-based Template Sensor with a unique_id, then delete the sensor via the UI, it would take care of housekeeping and delete the sensor’s stored data. However, it doesn’t do that either. Basically, there’s no way to get rid of a Trigger-based Template Sensor’s stored data (short of editing the hidden restoration file … which is what I initially did).

It is what also led me to add the ability to purge the attribute’s value via an event (for greater convenience).

sounds like a buggerooo

correct, I will adapt. have to find a way to get it to show nicely though, the pairing does take care of that :wink:

yes had noticed that, and tbh, I liked it, because the new sensor I used was intended to do so, and now I did not lose all of my historic data.
I can see though that in some situations this might actually be ‘unexpected’.

Not being able to delete that other than editing the hidden files would be an issue, though for that you also found a way as you describe. I think it would be a good idea to add that to the trigger entity documentation.

consider me guilty there, and I am very glad we have that now. It makes various things possible, like showing the last pressed button on state less entities.

those would be unknown on each restart otherwise. Now I have the last event, the last button, and the last time is was used.

I want the trigger sensor to count the number of times the automation gets triggered, or, more specifically, when the automation fires the action block (so its last_triggered is changed).

.

if Id like to do that on this trigger:

  - trigger:

      - platform: state
        entity_id: counter.reload_tradfri_integration
        to: 0

how then can we reset the state if the existing trigger template sensor? Only trying to reset the state, the list of attributes can remain untouched, that is, it could log the time of the reset, but need whipe the complete history

would this be allowed:

  - trigger:

      - platform: state
        entity_id: automation.reload_tradfri_integration #counter.
        attribute: last_triggered
        id: count

      - platform: state
        entity_id: counter.reload_tradfri_integration
        to: 0
        id: reset


    sensor:

      - unique_id: count_reload_tradfri_integration_single_attribute
        name: Count reload Tradfri integration single attribute
        icon: mdi:eye-plus-outline
        state: >
          {% if trigger.id == 'count' %}
          {{this.state|default(0)|int(0)+ 1}}
          {% else %} 0
          {% endif %}