Heads up: 2023.6 longer has persistent notifications in states: what to do?

why are you using an event… those are created by the services (in the next release). Oh I see what he’s attempting to do.

You can template in event_data, not sure why it’s not working for you. It may be an UI thing, switch it to yaml. It may even rquire the old event_data_template

1 Like

Thanks @petro - it is a UI thing, got it working.

Thanks to all for assistance

just for interest…seems a cool thing to try and continues to build stuff in your kit bags of ideas - before reworking my set up for this approach was going to wait to see where the upcoming changes end up (have read the PR’s but not fully processed what this means, I’ll have a play with the beta tomorrow! Thanks again

so with the new beta Ive moved this to use the actual notification events, and made it up to:

template:
  - trigger:
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: create
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: dismiss
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: dismiss_all
    sensor:
      - unique_id: persistent_notifications_overview
        name: Persistent notifications

        state: >
          {{ now().timestamp() | timestamp_custom() }}
        attributes:
          notifications: >
            {% set msgs = this.attributes.get('notifications', []) %}
            {% if trigger.event.data.service == 'create' %}
              {% set new = [{
                  "id": trigger.event.data.service_data.notification_id | default('TS' ~ now().timestamp()),
                  "title": trigger.event.data.service_data.title | default(''),
                  "message": trigger.event.data.service_data.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}
            {% elif trigger.event.data.service == 'dismiss' %}
              {% if trigger.event.data.service_data.notification_id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.event.data.service_data.notification_id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}
            {% else %}
              {{ [] }}
            {% endif %}

the service_data had to be changed accordingly but now it builds that attributes list nicely.

I must have made a typo, because I can not get the dismissed notification out of the attributes list. When dismissing to 0 notifications does not clear the sensor. Using the UI dismiss all does in fact reset the sensor.

so it seems the trigger.event.data.index is not functional as expected

first I tried to use:

        event_data:
          domain: persistent_notification
          service:
            - create
            - dismiss
            - dismiss_all

but unfortunately that is not accepted (no config error, it simply remains unchanged)

what I would most fervently love to see is an attribute with the number of currently notifying notifications
. Hopefully, doing that without repeating the complete attribute above.

check:

      - unique_id: persistent_notifications_overview
        name: Persistent notifications
        state: >
          {{this.attributes.notifications|count}}
        attributes:
          last_changed: >
            {{now().isoformat()}}

providing the number of notifications (not yet working dor dismiss/dismiss_all) and an additional attribute for the last change (as that was in the state in Taras’s first suggestion)

changing the state to:

        state: >
          {{this.attributes.notifications|count if trigger.event.data.service in ['create','dismiss']
            else 0 }}

almost makes it happen…
dismiss_all resets to 0 alright.

however, adding another notification only starts counting after the first additional notification, making the state trail 1 compared to the actual number of notifications in the list (and notification icon in the tray)

even though

which would both need to trigger at the same time

apparently this is suffering from the fact this variable is the ‘before’ state and not the actual state. But, even when verbosely using the entity_id it behaves the same…

all in all, some serious progress (thx Taras!) , and I need to fix some more…

  • number of notifications
  • dismiss count down/delete from list

It may be the 2am side of things talking but reading through the posts I’m unsure if a viable workaround has been found yet for the user case of holding automations for TTS to read them like how you could easily make TTS read persistent notification 1 2 3 4 etc in a row no matter which persistent notifications went off. The requirement for IDs rather than having them as a possible option on top of the +1 system seems to still be a thing.

Just as a reference point, this is what I have now, Its still not 100% correct, most importantly the index in the else is not functional, and (core) notifications are not deleted from the template when dismissed using the UI:

template:

  - sensor:

      - unique_id: persistent_notifications_count
        state: >
          {% set msgs = state_attr('sensor.persistent_notifications','notifications') %}
          {{msgs|count}}

  - trigger:
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: create
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: dismiss
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: dismiss_all
    sensor:

      - unique_id: persistent_notifications_overview
        name: Persistent notifications
        device_class: timestamp
        state: >
          {{now()}}
        attributes:
          notifications: >
            {% set msgs = this.attributes.get('notifications', []) %}
            {% if trigger.event.data.service == 'create' %}
              {% set new = [{
                  "id": trigger.event.data.service_data.notification_id | default('TS' ~ now().timestamp()),
                  "title": trigger.event.data.service_data.title | default(''),
                  "message": trigger.event.data.service_data.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}

            {% elif trigger.event.data.service == 'dismiss' %}
              {% if trigger.event.data.service_data.notification_id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.event.data.service_data.notification_id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}

            {% else %}
              {{ [] }}
            {% endif %}

  - binary_sensor:

      - unique_id: binary_persistent_notifications
        device_class: problem
        state: >
          {{states('sensor.persistent_notifications_count')|int(default=0) != 0}}
        icon: >
          mdi:message-bulleted{{'-off' if this.state == 'off'}}

the binary is ok, but it is also frustrated by the fact the state of the source sensors is not correct yet in all circumstances

moving that counter to a dedicated template sensor seemed simplest, and we can again use what Taras envisioned above in the state, but I changed it to being a timestamp sensor using:

        state: >
          {{now()}}

so we can use the nicer representation of the entity in the Frontend:

  - entity: binary_sensor.persistent_notifications
    secondary_info: last-changed
  - entity: sensor.persistent_notifications_count
    secondary_info: last-changed
    format: relative
  - entity: sensor.persistent_notifications
    secondary_info: last-changed
# and a conditional Markdown, iterating the list of notifications
  - type: custom:hui-element
    card_type: conditional
    conditions:
      - entity: binary_sensor.persistent_notifications
        state: 'on'
    card:
      type: custom:hui-element
      card_type: markdown
      card_mod:
        style: |
          ha-card {
            box-shadow: none;
            margin: 0px -16px;
          }
      content: >
        {% set count = states('sensor.persistent_notifications_count') %}
        #### {{count}} Notification{{'' if count == '1' else 's'}}:
        {{'\n'}}
        {% for m in state_attr('sensor.persistent_notifications','notifications') if m %}

        {{m.id}}
        {{m.title}}
        {{m.message}}{{'\n'}}

        {% else %} Geen notificaties
        {% endfor %}

taking out the need for my additional attribute. ! problem down, 1 left to go:

now, how to get those notifications dismissed correctly. above template deletes notifications using the dev tools and using a notification_id, either manually set ‘test123’ or the one set by the default.

It does Not delete individual notifications using the UI tray notification Dismiss button.

we can, I repeat, dismiss all, either programmatically using the new service, or the UI tray button,

I’ve gone a slightly different path. Working on @123’s method but using the following automation to “mirror” the persistant notifications to sensor.messages:

alias: Persistent Notification Mirror
description: ""
trigger:
  - platform: persistent_notification
    update_type:
      - added
      - current
      - updated
    id: added
  - platform: persistent_notification
    update_type:
      - removed
    id: removed
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - added
        sequence:
          - event: message_create
            event_data:
              id: "{{ trigger.notification.notification_id }}"
              title: "{{ trigger.notification.title }}"
              message: "{{ trigger.notification.message }}"
      - conditions:
          - condition: trigger
            id:
              - removed
        sequence:
          - event: message_delete
            event_data:
              id: |
                {{ trigger.notification.notification_id }}
mode: queued
max: 30

The thing here is to have the mode queued or parallel so that when the Dismiss All is called from the UI or via a service the automation repeats each removed trigger and keeps everything aligned - still need to test edge cases but so far holding up nicely.

One change I did make to 123’s template was on create, I check for the existence of a message with the same ID and delete the original first (this could be the way I had my config set up but for example I generate a persistent notification for unavailable devices and list them - if a another device becomes unavailable I re-trigger the notification on the same ID, the persistent notifications replace it but was ending up with duplicates in the mirror messages):

template:
  - trigger:
      - platform: event
        event_type:
          - message_create
          - message_delete
          - message_delete_all
    sensor:
      - name: Messages
        state: "{{ now().timestamp() | timestamp_custom() }}"
        attributes:
          messages: >
            {% set msgs = this.attributes.get('messages', []) %}
            {% if trigger.event.event_type == 'message_create' %}
>>>           {% if trigger.event.data.id is defined %}   <<<--- ADDED THIS IF STATEMENT
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.event.data.id) | list %}
              {% endif %}  
              {% set new = [{
                  "id": trigger.event.data.id | default('TS' ~ now().timestamp()),
                  "title": trigger.event.data.title | default(''),
                  "message": trigger.event.data.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}          
            {% elif trigger.event.event_type == 'message_delete' %}
              {% if trigger.event.data.id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.event.data.id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}
            {% else %}
              {{ [] }}
            {% endif %}

Still working on the front end display but rolling with something like this (POC currently and needs styling nicely to match my set up etc…):

image

type: markdown
content: |-
  {%- if state_attr('sensor.messages','messages') | list | count == 0 %}
  No Notifications
  {%- else %}
    {%- for item in state_attr('sensor.messages', 'messages') %}
      {%- if not loop.first %}<hr> {% endif -%}
        <table width="100%">
        <tr>
        <td width="30%"><font color='#03a9f4'>{{item.title}}</font></td>
        <td>{{item.message}}</td>
        </tr>
        <tr>
        <td></td>
        <td>{{ item.time | as_datetime | relative_time }} ago</td>
        </td>      
        </table>
    {%- endfor %}
  {%- endif %}
card_mod:
  style: |
    ha-card {
      box-shadow: var(--sl-shadow);
      }

EDIT: I’ve made the assumption that all persistent notifications have an ID, ones I manually raise do and assumed systems ones will, should I find a case that doesn’t the dismiss all wouldn’t work in this configuration I think

1 Like

I see I completely missed those new triggers Automation Trigger - Home Assistant

however, there is no dedicated trigger for dismiss_all, so I guess I have to keep that event trigger in the template? or would it also fire on dismiss_all… not sure what the effect would be there.

in your case, what happens when clicking the UI notification dismiss? does it actually decrease the number of the counter, and the list in the template?

also, on those triggers:
what do current and updated indicate? Ive seen them in the code, but they are not listed in the services, so I am a bit in the dark why/when we would use those

fear a bit that updated is also valid when a deleted action takes place, which would upset the template :wink:

You will get a remove for each notification you have when you dismiss_all. So 100 notifications will be 100 triggers. Make sure your automation is parallel :wink:

FYI, you can spend 5 minutes creating a simple automation to test the functionality and dispel your fears.

this is now working on all accounts:

  - trigger:
      - platform: persistent_notification
        update_type: added
        id: added
      - platform: persistent_notification
        update_type: removed
        id: removed
#       - platform: event
#         event_type: call_service
#         event_data:
#           domain: persistent_notification
#           service: create
#       - platform: event
#         event_type: call_service
#         event_data:
#           domain: persistent_notification
#           service: dismiss
      - platform: event
        event_type: call_service
        event_data:
          domain: persistent_notification
          service: dismiss_all
    sensor:

      - unique_id: persistent_notifications_overview
        name: Persistent notifications
        device_class: timestamp
        state: >
          {{now()}}
        attributes:
          notifications: >
            {% set msgs = this.attributes.get('notifications', []) %}
            {% if trigger.id == 'added' %}
              {% set new = [{
                  "id": trigger.notification.notification_id | default('TS' ~ now().timestamp()),
                  "title": trigger.notification.title | default(''),
                  "message": trigger.notification.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}

            {% elif trigger.id == 'removed' %}
              {% if trigger.notification.notification_id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.notification.notification_id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}

            {% else %} {{ [] }}
            {% endif %}

btw, I have not a single automation here, its all the trigger template, and a binary for the alerts, and a single template sensor for the count

1 Like

No, but set the automation to queued or parallel - the Dismiss All generates a trigger of dismiss for each Persistent Notification individually so it works.

Yes, I assume you mean in the popout persistent notification panel? The template sensor.messages (or whatever you’ve called it) is updated by the automation.

Not sure, not hit a case where I’ve seen these triggered yet - but I wrapped them up in the create (and modified the create to delete any notification/message with the same ID first so that everything stays in sync).

keep in mind that you’ll also have to raise the max number of automations running at a time.

max: 100

Dismissing 100s of messages might have a rolling 50 to 100 automations running at the same time. Depends on the speed of your system.

1 Like

sorry, but what automation are you referring to?
I’ve have none for this notification ledger

template sensors wont have this issue. Automations will. There’s a max number of parallel automations that can run at a time, the default is 10. If you use this in an automation, make the number large.

FYI

  - trigger:
      - platform: persistent_notification
        update_type:
        - added
        - removed
    sensor:

      - unique_id: persistent_notifications_overview
        name: Persistent notifications
        device_class: timestamp
        state: >
          {{now()}}
        attributes:
          notifications: >
            {% set msgs = this.attributes.get('notifications', []) %}
            {% if trigger.update_type == 'added' %}
              {% set new = [{
                  "id": trigger.notification.notification_id | default('TS' ~ now().timestamp()),
                  "title": trigger.notification.title | default(''),
                  "message": trigger.notification.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}

            {% elif trigger.update_type == 'removed' %}
              {% if trigger.notification.notification_id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.notification.notification_id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}

            {% else %} {{ [] }}
            {% endif %}

ok thanks.

for now, besides some formatting of the markdown, I believe we’ve got the complete package:

  • count the number of active persistent notifications (sensor.persistent_notifications_count)

  • alert on notifying notifications (binary_sensor.persistent_notifications)

  • display list of notifications (attribute on sensor.persistent_notifications)

  • all updated on programmatic and manual creation/dismissal

  • working for core and manual notifications

  • dismiss_all service in core

  • no mirroring system required for ‘messages’, though people might like that and can follow what Mike does above with Taras’s first suggestion
    very nice indeed

thanks to all involved (@bdraco @petro @123 @woodmj74 )

template:

  - sensor:

# use `| default([], true)` to apply the default when the input is none
# or [] ...
      - unique_id: persistent_notifications_count
        state: >
          {% set msgs = state_attr('sensor.persistent_notifications','notifications') or [] %}
          {{msgs|count}}

  - trigger:
      - platform: persistent_notification
        update_type: added
        id: added
      - platform: persistent_notification
        update_type: removed
        id: removed

    sensor:

      - unique_id: persistent_notifications_overview
        name: Persistent notifications
        device_class: timestamp
        state: >
          {{now()}}
        attributes:
          notifications: >
            {% set msgs = this.attributes.get('notifications', []) %}
            {% if trigger.id == 'added' %}
              {% set new = [{
                  "id": trigger.notification.notification_id | default('TS' ~ now().timestamp()),
                  "title": trigger.notification.title | default(''),
                  "message": trigger.notification.message | default(''),
                  "time": now().isoformat() }] %}
              {{ (msgs + new) }}

            {% elif trigger.id == 'removed' %}
              {% if trigger.notification.notification_id is defined %}
                {% set msgs = msgs | rejectattr('id', 'eq', trigger.notification.notification_id) | list %}
              {% elif trigger.event.data.index is defined and trigger.event.data.index | is_number %}
                {% set t = trigger.event.data.index | int(0) - 1 %}
                {% if 0 <= t < msgs|count %}
                  {% set msgs = msgs | rejectattr('id', 'eq', msgs[t].id) | list %}
                {% endif %}
              {% endif %}
              {{ msgs }}

            {% else %} {{ [] }}
            {% endif %}

  - binary_sensor:

      - unique_id: binary_persistent_notifications
        device_class: problem
        state: >
          {{states('sensor.persistent_notifications_count')|int(default=0) != 0}}
        icon: >
          mdi:message-bulleted{{'-off' if this.state == 'off'}}
1 Like

why do you keep using

you don’t need that. The removed will handle what dimiss_all is doing. You simply need

      - platform: persistent_notification
        update_type: 
        - added
        - removed

and remove your else statement.

was a c&p.
I’ve edited above

can I leave out the else, and still have the sensor count correctly? figured Id need that empty list to count 0

if I condense those 2 triggers (love that) I can not use the trigger.id. have to change that inside the template then, let me see.

I’m reworking the template now. There’s a bunch of things that aren’t correct anymore in that template. Like all notifications via that trigger will have a notification_id