Restoring persistent notifications on restart

Edit (27th February): Please update to the latest version below
I found an issue with my original version of this script which causes a problem with persistent notifications with ids containing hyphens which, unfortunately, includes the “Login attempt failed” notification. The updated version replaces the hyphen with an underscore when determining the entity id of the notification to fix this.


I’ve starting using persistent notifications a lot, and I needed a way to make them persist if Home Assistant restarts. I created some automations for this, as a package:

persistent_notification_restoration.yaml (2.0 KB)

There are three automations:

  • save_notification_create - saves notifications when they are created by posting a retained message to an MQTT topic
  • save_notification_dismiss - removes the retained message when a notification is dismissed
  • restore_notifications - subscribes to the topic and creates persistent notifications if a notification doesn’t already exist for the notification id or the message has changed (I missed this bit in my first attempt and ended up with the notification being sent repeatedly to the MQTT topic, because it was re-triggering the save automation!)

To add this to Home Assistant:

1) Configure MQTT if you don’t already have it configured.
2) Create a packages folder in your config folder and add the following under homeassistant: in your config file:
packages: !include_dir_named packages
3) add the persistent_notification_restoration.yaml file in your packages folder.

Alternative, you can copy the automations from the file and add them to your config file if you don’t want to use packages.

Note: The first automation has a condition to exclude the notification on new devices being discovered (id config_entry_discovery), Without that, these notifications were appearing on every restart (only with the automations enabled). I suspect that something in the restart process causes it to erroneously detect that there are new devices, but this is not normally noticed because the notification was previously lost once it had restarted.

4 Likes

Cool idea! Thanks alot!

EDIT: @Steven_Rollason, works for me only when i disable the condition and on dismiss i’m getting error in the log.

2019-02-22 23:13:56 ERROR (MainThread) [homeassistant.helpers.condition] Error during template condition: UndefinedError: 'dict object' has no attribute 'payload_json'

I have updated the file, can you try the updated version? There was a comment in the file and I think this was causing the condition on the save automation. I have also updated the condition on the restore automation so it ignores notifications being dismissed (it gets an empty payload for these and this was causing the error you were seeing).

Yeah, works now as expected.
Thanks once again!

I have uploaded another update to the automations which fixes a problem with notifications with hyphens in their ids. These would keep coming back immediately after being dismissed. Unfortunately, the “Login attempt failed” notification has a hyphen in its id and so, for a while, I thought I was being hacked :grin:

Ha! :grinning: Had the same issue yesterday with a ‘Invalid config notification’.
There was a loop and the CPU raised to 80%, had to restart the host.

I decided to restore only notifications that i create on my own with notification_id myha_....

    condition:
      - condition: template
        value_template: >
          {{ trigger.event.data.service_data.notification_id[:5] == "myha_" }}

in create and dismiss automation.

I got a bunch of errors after updating the other day and removing your package fixed it.

I just downloaded the new package and I’m still getting the same errors. It’s the same error over and over every second or so forever:

2019-02-27 13:33:20 ERROR (MainThread) [homeassistant.components.automation] Error while executing automation automation.restore_notifications. Unknown error for call_service at pos 1: 
Traceback (most recent call last):
  File "/usr/src/app/homeassistant/components/automation/__init__.py", line 375, in action
    await script_obj.async_run(variables, context)
  File "/usr/src/app/homeassistant/helpers/script.py", line 131, in async_run
    await self._handle_action(action, variables, context)
  File "/usr/src/app/homeassistant/helpers/script.py", line 210, in _handle_action
    action, variables, context)
  File "/usr/src/app/homeassistant/helpers/script.py", line 299, in _async_call_service
    context=context
  File "/usr/src/app/homeassistant/helpers/service.py", line 85, in async_call_from_config
    domain, service_name, service_data, blocking=blocking, context=context)
  File "/usr/src/app/homeassistant/core.py", line 1133, in async_call
    self._execute_service(handler, service_call))
  File "/usr/src/app/homeassistant/core.py", line 1153, in _execute_service
    handler.func(service_call)
  File "/usr/src/app/homeassistant/components/persistent_notification/__init__.py", line 138, in create_service
    hass.states.async_set(entity_id, STATE, attr)
  File "/usr/src/app/homeassistant/core.py", line 915, in async_set
    context)
  File "/usr/src/app/homeassistant/core.py", line 677, in __init__
    "Format should be <domain>.<object_id>").format(entity_id))
homeassistant.exceptions.InvalidEntityFormatError: Invalid entity id encountered: persistent_notification.. Format should be <domain>.<object_id>

I’ve looked to see if i can find the line that’s causing it but no luck so far…

I’ve tried to comment them out one at a time.

If I comment out the first automation I get the above error only once.

If I comment out the second one I get the error forever in a loop.

If I comment out the third one I don’t get any errors at all.

So there is definitely some thing going on here but I can’t narrow it down to exactly what the problem is.

Do you have any other devices that publish to ‘home-assistant/notifications/+’ ?

no just this package.

I think I’ve worked out what was causing your issue. This appears to happen if you create persistent notifications without passing a value for notification_id.
The automation works by listening for calls to the persistent_notification.create service. If you don’t pass a notification id when creating the notification, the service call won’t include the notification_id parameter. The automation relies on this to uniquely identify the notification.

Home Assistant automatically assigns a notification id (in the format “notification”, “notification_2”, etc.) if you don’t include it in the service call, but there is no way I can see for the automation to find which id has been assigned, so the best thing to do is to always assign notification ids if you want to use these automations.

I’ve done a search in all of my config files for “persistent_notification.create” and I have that service call in only three places in my config.

One is in a custom component that creates a persistent notification on a failed log in attempt. I don’t know if that creates a notification_id but I don’t think it does. Either way, I commented out the sensor.autenticated in my config and restarted and still have the exact same unending errors as posted above.

I have another location where I call that service but it is assigned a notification_id:

- service_template: >
    {% if states.sensor.nws_alerts.state != '0' %}
      persistent_notification.create
    {% endif %}
  data_template:
    notification_id: "nwswxalert"
    message: "{{ message }}"
    title: '{{ title }}'

The last place I have that service call is in your last automation of your package.

So, I found this thread and tried to use your automations. I am getting a similar error to another commentor:

Error while executing automation automation.restore_notifications. Unknown error for call_service at pos 1:
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/components/automation/init.py”, line 437, in action
await script_obj.async_run(variables, context)
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 190, in async_run
await self._handle_action(action, variables, context)
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 274, in _handle_action
await self._actions[_determine_action(action)](action, variables, context)
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 357, in _async_call_service
context=context,
File “/usr/src/homeassistant/homeassistant/helpers/service.py”, line 97, in async_call_from_config
domain, service_name, service_data, blocking=blocking, context=context
File “/usr/src/homeassistant/homeassistant/core.py”, line 1236, in async_call
await asyncio.shield(self._execute_service(handler, service_call))
File “/usr/src/homeassistant/homeassistant/core.py”, line 1259, in _execute_service
handler.func(service_call)
File “/usr/src/homeassistant/homeassistant/components/persistent_notification/init.py”, line 139, in create_service
hass.states.async_set(entity_id, STATE, attr)
File “/usr/src/homeassistant/homeassistant/core.py”, line 988, in async_set
state = State(entity_id, new_state, attributes, last_changed, None, context)
File “/usr/src/homeassistant/homeassistant/core.py”, line 725, in init
).format(entity_id)
homeassistant.exceptions.InvalidEntityFormatError: Invalid entity id encountered: persistent_notification… Format should be .<object_id>

It seems like this part of your restoration notification may be causing it:

value_template: >- {{ trigger.payload != '' and state_attr("persistent_notification." + trigger.payload_json.notification_id | replace("-","_"),"message") != trigger.payload_json.message }}

I may be wrong but isn’t that referring to persistent_notification as an entity domain? Sorry I’m not an expert with this stuff.

Persistent notifications also appear as entities. The issue may be that you have a notification with a notification_id which isn’t a valid entity id. That template already deals with notification IDs contains dashes, but this notification may have something else which makes it invalid. Can you do the following?

  1. Open Developer Tools > States
  2. Filter the list on persistent_notification
  3. Check if there is anything with an odd entity id and see what its notification_id attribute is

Ok so, I just tested it with only one notification.

persistent_notification.2019_12_16_12_25
state: notifying
title:
message: Reminder: Payday (12:25 PM)

I get this error upon restart:

Error while executing automation automation.restore_notifications. Unknown error for call_service at pos 1: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/components/automation/__init__.py", line 437, in action await script_obj.async_run(variables, context) File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 190, in async_run await self._handle_action(action, variables, context) File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 274, in _handle_action await self._actions[_determine_action(action)](action, variables, context) File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 357, in _async_call_service context=context, File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 97, in async_call_from_config domain, service_name, service_data, blocking=blocking, context=context File "/usr/src/homeassistant/homeassistant/core.py", line 1236, in async_call await asyncio.shield(self._execute_service(handler, service_call)) File "/usr/src/homeassistant/homeassistant/core.py", line 1259, in _execute_service handler.func(service_call) File "/usr/src/homeassistant/homeassistant/components/persistent_notification/__init__.py", line 139, in create_service hass.states.async_set(entity_id, STATE, attr) File "/usr/src/homeassistant/homeassistant/core.py", line 988, in async_set state = State(entity_id, new_state, attributes, last_changed, None, context) File "/usr/src/homeassistant/homeassistant/core.py", line 725, in __init__ ).format(entity_id) homeassistant.exceptions.InvalidEntityFormatError: Invalid entity id encountered: persistent_notification.. Format should be <domain>.<object_id> Connection lost. Reconnecting…

So, after restart, the notification is restored. But it cannot be dismissed unless I turn off your automations.

Also, if these automations are on, I am no longer able to access my instance through Nubu Casa cloud. I have to go to my IP address to access it… Wierd.

edit: When I pull up automation.save_notifications_on_create in States it shows that it is being triggered constantly. the "last triggered’ attribute is just a running clock because it is constantly being triggered. When I listen to the mqtt topic it is the same thing.

Would you be able to log onto your MQTT broker and see what shows up as the notifcation id?
Judging from the entity id, I think it may be something like “2019-12-16 12:25” so changing the value template like this may work:

value_template: >- 
  {{ trigger.payload != '' and state_attr("persistent_notification." + trigger.payload_json.notification_id | 
  replace("-","_") | replace(" ","_") | replace(":","_"),"message") != trigger.payload_json.message }}

The notification id is “2019-12-16, 13:10”. I tried your new template and it didn’t help, and then I tried adding a replace for the comma too and that didn’t help. The restore automation is still throwing the same error I quoted once every millisecond basically.

Weirdly, the restoring does work even though it throws that error. It restores the notification. It just won’t let me dismiss it because it restores it right away as soon as I dismiss it.

edit: Okay so the problem is definitely with the notification Id. I changed it to just a single word and everything is now working. However i’m still getting that error in my logs over and over again. Now the issue is trying to figure out how to template a discrete ID for each notification without using the time_date sensor since I get multiple notifications from several of my automations…

Still wondering why it is throwing that error even though it works.

edit 2: looks like the random number sensor works for ID’s… so I guess this issue is resolved except for the weird error message.

It seems that the problem is that the function that Home Assistant uses to turn ids into correct entity ids (slugify) doesn’t just do a simple replace of the unsafe characters with underscores like my template does. For one thing, if there are two unsafe characters in a row slugify will replace them with a single underscore while my template will result in two underscores. I’ll see if there is a better way of doing this, but here is a workaround in the meantime:

value_template: >- 
  {{ trigger.payload != '' and state_attr("persistent_notification." + trigger.payload_json.notification_id | 
  replace("-","_") | replace(" ","_") | replace(":","_") | replace(",","_") | replace("__","_"),"message") != trigger.payload_json.message }}

That template will first replace the dash, space, colon, and comma with underscores and then replace any double underscores with a single underscore.

I just checked my mqtt broker and the error seems be caused by this:

{ "notification_id": "", "title": "", "message": "testing" }

which was a notification I created to test this before realizing I needed a notification ID. The error had occured over 29k times and since I most recently restarted. Since there was no id to dismiss, I had to reinstall Mosquito MQTT to clear it. No more errors and everything seems to be working now.

So correct me if I’m wrong here, but this series of automations is set up as a perpetual loop right? The save automation is triggered when the persistent notification service is called… and the restore automation calls that service which then triggers the save automation, which then publishes the mqtt message which triggers the restore automation again, and so on. So if somebody has a message published which these automations can’t understand, then essentially the restore automation is being asked to restore something that does not exist (as far as it knows), thus getting stuck in basically a feedback loop since a notification without an Id cannot be dismissed. Does that sound right?

That’s why i decided to restore only my messages.

Should this replace the original condition, or be used in addition as an AND condition?

also what does the [:5] indicate? I added this as an and condition and none of my notifications are being saved now. I have the ID’s set to be

"kyle_{{ states.sensor.random_sensor.state }}"

and I have the condition set to “kyle_” instead of “myha_”. Any thoughts on what I may be doing wrong?