Simple UI for making backups of Add-ons

If you normally make a backup of an Add-on before upgrading it, you might be interested in the following application that simplifies the process. 1

1. Create Template entities

Create the following Template Select and Trigger-based Template Sensor entities. The select entity will automatically populate itself with the names of add_ons associated with the Supervisor integration.

select.add_ons, sensor.add_ons_selection
template:
  - trigger:
      - platform: event
        event_type: custom_add_ons_selection
    sensor:
      - name: Add Ons Selection
        unique_id: custom_add_ons_selection_helper
        state: "{{ trigger.event.data.value }}"

  - select:
      - name: "Add Ons"
        state: "{{ states('sensor.add_ons_selection') }}"
        options: >
          {% set x = integration_entities('Supervisor')
            | select('match', 'update')
            | reject('match', 'update\.home_assistant')
            | list %}
          {% set z = zip(x | map('device_attr', 'name') | list,
            x | map('device_attr', 'name_by_user') | list) | list %}
          {{ (z | select('contains', none) | map(attribute=0) | list
            + z | reject('contains', none) | map(attribute=1) | list)
            | sort }}
        select_option:
          - event: custom_add_ons_selection
            event_data:
              value: "{{ option }}"

2. Create script

Create the following script.

script.backup_add_on
  backup_add_on:
    alias: Backup Add On
    description: Backup selected add-on
    mode: single
    sequence:
      - variables:
          id: "{{ device_id(states('select.add_ons')) }}"
      - if: "{{ id is not none }}"
        then:
          - variables:
              add_on: "{{ (device_attr(id, 'identifiers') | list)[0][1] }}"
              label: >
                  {{ "addon_{}_{}_{}".format(
                    (device_attr(id, 'identifiers') | list)[0][1],
                    device_attr(id, 'sw_version'),
                    (now() | string)[:-13]) }}
          - action: hassio.backup_partial
            data:
              homeassistant_exclude_database: true
              compressed: true
              addons: "{{ add_on }}"
              name: "{{ label }}"
        else:
          - action: notify.persistent_notification
            data:
              title: Invalid Add-On Selection
              message: The selected add-on does not exist.

3. Create cards

Create the UI with an Entities Card and a Button Card. The following example puts the two cards within a Vertical Stack card but youā€™re free to arrange them in whatever way you prefer.

Cards
type: vertical-stack
cards:
  - type: entities
    entities:
      - entity: select.add_ons
  - show_name: true
    show_icon: false
    type: button
    tap_action:
      action: toggle
    entity: script.backup_add_on

Usage

To backup an Add-on, select the desired Add-on from select.add_ons then click the ā€œBackup Add Onā€ button. The resulting backup will be labeled in this format:

addon_a0d7b954_nodered_18.1.1_2025-01-09 10:07:55

Please be advised that I have not yet tested this in version 2025.1.X. You shouldnā€™t encounter any problems but if you do, I wonā€™t be able to fix them until the end of January (when I plan to upgrade to 2025.1.X).

Footnotes

  1. As of version 2025.1.1, the backup toggle button has been removed from the Update panel. It eliminates the convenience of optionally creating a backup of the Add-on prior to upgrading it. The current alternative is to use hassio.backup_partial (in a script or in Developer Tools ā†’ Actions).

Versions

2025-01-15 Supports renamed Add-ons.
The Template Select in the initial version listed each Add-onā€™s default name. If the user changed the Add-onā€™s name, it wasnā€™t shown in the Template Select. As a consequence, the script failed to find the Add-on (because it searched using the default name).
In this version, the Template Select lists an Add-onā€™s modified name (if the user modified it, otherwise it shows the default name).

15 Likes

seems to be working fine on 2025.1.1ā€¦backing up anyway (havenā€™t tried a restore)ā€¦thanks Taras!

update - just to add that I backed up Mosquitto broker (core-mosquitto) with this prior to a pending update both as FYI and in light of comments below

1 Like

Really appreciate this, but Iā€™ve run into a couple of issues which I canā€™t code around.

  • The options template for the select rejects any addons with ā€˜Home Assistantā€™ in the name, such as the ā€˜NGINX Home Assistant SSL proxyā€™

  • Iā€™ve stumbled on an apparent anomaly in translating device attribute name from the select, to device attribute id in the script (all tested in Dev Tools). For example, the device_attr function returns File editor with a lowercase e for the name attribute for the ā€˜File editorā€™ addon. But the device_id function only returns a value from the string File Editor with an uppercase E. I have other addons like this, such as ā€˜chronyā€™. On the other hand, with the NGINX addon mentioned above, the device_id function works with NGINX Home Assistant SSL proxy (lowercase p on ā€˜proxyā€™), but not NGINX Home Assistant SSL Proxy (with uppercase P on ā€˜Proxyā€™). Baffled!

1 Like

Thatā€™s by design to prevent Supervisor and Core from appearing in the select entity.

Each Add-on has a corresponding update entity. The template selects all update entities then rejects all containing the string home-assistant. This serves to suppress the display of Supervisor and Core.

To suppress them yet allow displaying other Add-ons (containing home_assistant), change the second line in the template to this:

| reject('match', 'update\.home_assistant')

It rejects any update entity whose object_id begins with home_assistant.

On the other hand, if you want to display Supervisor and Core, simply remove the entire line containing the reject filter.

I have the File editor Add-on but, on my system (2024.12.5), I cannot replicate the anomaly you encountered.

device_attr() reports its name as ā€˜File editorā€™.

This returns a valid alphanumeric string.

{{ device_id('File editor') }}

This returns None

{{ device_id('File Editor') }}

I will examine the issue again after I upgrade (in a few weeks).

1 Like

Thanks so much for the tweak to the select template, works perfectly!

On the other issue, Iā€™ve been looking further into this, and digging around core.device_registry. It seems that the anomalous Add-ons have an attribute name_by_user, which in my case for the File editor Add-on is set to File Editor. This is obvously self-inflicted, as I must have renamed it via Settings > Devices. It seems the device_id function looks for this first, but its value is null if not set.

Is there a neat way to rewrite the select options template such that it takes the deviceā€™s name_by_user attribute where it exists, but defaults to the name attribute? I could only find a way using a {% for %} loop and a namespace, which seems a lot less elegant. Otherwise, I shall just rename the Add-ons back - must have been an especially nitpicky day when I did this.

I have never renamed any of my Add-ons so hadnā€™t considered the possibility it might cause a potential problem for the conversion from an Add-onā€™s (user modified) name to its device_id.

So if name_by_user has a string value, it supercedes the existing name attribute. As a result, device_attr( 'ABC123', 'name') returns a string that fails to be usable with device_id() which only works with the Add-onā€™s actual name value and not name_by_user.

One might conclude that device_id is at fault because it ought to work correctly with either name or name_by_user.

Please post the template you created.

Sure, no problem:

{%- set addon_list =
  integration_entities('Supervisor')
  | select('match', 'update')
  | reject('match', 'update\.home_assistant')
  | sort | list
%}
{%- set addon_name_filtered_list = namespace(value=[]) %}
{%- for addon in addon_list %}
  {%- if device_attr(addon, 'name_by_user') != None %}
    {%- set addon_name_filtered_list.value = addon_name_filtered_list.value + [device_attr(addon, 'name_by_user')] %}
  {%- else %}
    {%- set addon_name_filtered_list.value = addon_name_filtered_list.value + [device_attr(addon, 'name')] %}
  {%- endif %}
{%- endfor %}
{{ addon_name_filtered_list.value }}

Can you tell me how you are renaming the Add-on?

Hereā€™s the test I performed:

  1. I installed a new Add-on call ā€œGit pullā€.
  2. I didnā€™t configure it.
  3. I restarted Home Assistant.
  4. The template reported ā€œGit pullā€ in its list.
  5. I located ā€œGit pullā€ in Settings ā†’ Devices & Services ā†’ Devices and renamed it to ā€œGit Pullerā€ and allowed it to rename all 6 associated entities.
  6. I restarted Home Assistant.
  7. The template still reports ā€œGit pullā€ in its list (not ā€œGit Pullerā€).
  8. The Add-onā€™s associated entities do contain git_puller in their entity_id.
  9. I reconfirmed that in Settings ā†’ Devices & Services ā†’ Devices, itā€™s displayed as ā€œGit Pullerā€.

All this to say that, after renaming, I canā€™t replicate the anomaly you reported. Perhaps my test is flawed?

What do these return in Developer Tools?

{{ device_id("Git pull") }}

{{ device_id("Git Puller") }}

Maybe Iā€™m just hallucinating?!

Well now thatā€™s the additional step I missed in my testing procedure!

The device_id for ā€œGit pullā€ is None whereas itā€™s an alphanumeric string for ā€œGit Pullerā€.

So the template reports the Add-onā€™s original name (not its user-modified name) but device_id fails to work with the original name (succeeds with the modified name).

Based on this peculiar behavior, ideally the template should contain an Add-onā€™s user-modified name, but only if it exists.

Yeah, that requirement complicates the template.

1 Like

Wherever you have the time, I would appreciate it if you could test the following on your system. It should produce a list of Add-on names. Any Add-onā€™s name that has been modified will have its modified name displayed (as opposed to its original name).

It works on my system but I need confirmation it produces correct data on yours (before I incorporate it into the Template Select).

{% set x = integration_entities('Supervisor')
  | select('match', 'update')
  | reject('match', 'update\.home_assistant')
  | list %}
{% set y = x | map('device_attr', 'name') | list %}
{% set z = x | map('device_attr', 'name_by_user') | list %}
{{ (zip(y,z) | select('contains', none) | map(attribute=0) | list
  + zip(y,z) | reject('contains', none) | map(attribute=1) | list)
  | sort
}}

Iā€™ve tested that on 2025.1.2 after editing the name of an Add-on via Devices (didnā€™t even know you could change their names :sunglasses:)ā€¦it works fine returning all the add-ons I have but showing the new/edited name for those changed

1 Like

Thanks for testing it.

Can you try this streamlined version?

{% set x = integration_entities('Supervisor')
  | select('match', 'update')
  | reject('match', 'update\.home_assistant')
  | list %}

{% set z = zip(x | map('device_attr', 'name') | list,
  x | map('device_attr', 'name_by_user') | list) | list %}

{{ (z | select('contains', none) | map(attribute=0) | list
  + z | reject('contains', none) | map(attribute=1) | list)
  | sort
}}

If it works correctly then thatā€™s what will be used in the Template Select.

1 Like

Yes, I can confirm this works for me as well, and each returned value maps correctly with the device_id function.

The zip function is a new one on me, learnt something here - many thanks!

EDIT: and the streamlined version too btw

1 Like

Itā€™s my first time finding a use for zip(). Itā€™s also the first time I have found a need for map(attribute=0) which selects the zeroth element of a tuple.

Thanks for confirming both versions work. Iā€™ll use the second one to update the Template Select.

2 Likes

know youā€™ve already confirmation on the streamlined version but just to doubly acknowledge that itā€™s working fine here too

1 Like

Works great - thank you @123 !

1 Like