Auto-update Home Assistant

So, I finally got some time to create a Blueprint to help keeping Home Assistant updated automatically.

ATTENTION: Before you jump to the Blueprint, please take a look on those discussions around this topic and make sure you understand the risks related to auto-updating Home Assistant:

Please provide feedback. It’s quite hard to extensively test this as updates are not easy to un-install. :slight_smile:

Change log:

  • 2022-06-08 07:10 UTC:
    • First time published
  • 2022-06-27 14:52 UTC:
    • Added an option to pause/override the automation based in an binary state entity (to avoid updates when your house is in party mode, as an example
    • Split update of core items (first) and OS (last).

This is the Blueprint:

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

blueprint:
  name: Home Assistant Auto-update
  description: 'Update Home Assistant automatically when a new update is available.
    
    Note 1 => HACS updates NOT included.
    
    Note 2 => Home Assistant may restart automatically as part of the update process.
    
    IMPORTANT => Use under your own risk. Please see the discussions on Home Assistant community around the risks involved on auto-updating the system.'
  domain: automation
  source_url: https://community.home-assistant.io/t/auto-update-home-assistant/429015
  input:
    backup_bool:
      name: Create a full backup before start the updates?
      description: Note => This automation will skip the backup if another backup was completed in the last two hours before starting the updates.
      default: true
      selector:
        boolean:
    schedule_time:
      name: Schedule time
      description: Remember that Home Assistant may restart after an update, so please avoid sensitive times.
      default: '03:00:00'
      selector:
        time:
    schedule_isoweekdays:
      name: Week days
      description: You can select multiple days of week for run the auto-updates.
      default: ['1','2','3','4','5','6','7']
      selector:
        select:
          #mode: list
          multiple: true
          options:
            - label: Monday
              value: "1"
            - label: Tuesday
              value: "2"
            - label: Wednesday
              value: "3"
            - label: Thursday
              value: "4"
            - label: Friday
              value: "5"
            - label: Saturday
              value: "6"
            - label: Sunday
              value: "7"
    schedule_monthday:
      name: Earliest day in the month to update Home Assistant.
      description: 'Usually a new major version of Home Assistant is available on the begining of every month. Some people consider those release as not stable enough and prefer to avoid those versions, not updating the system until the mid of the month (day 15).
        
        Note => If you select a day higher than 28 the updates won''t run every month.'
      default: 21
      selector:
        number:
          min: 1
          max: 31
          step: 1
          mode: slider
    update_exclusions:
      name: (optional) Exclusions
      description: 'Select the items that should NOT be included on the automated updates.
        
        => Use this if you want to keep some add-on on an specific version and avoid auto-updates to it.'
      default: false
      selector:
        entity:
          multiple: true
          domain: update
    actions_pre_update:
      name: (optional) Pre-update actions
      description: 'Actions to execute before the backup or any update starts.

        You can use this to send notifications, turn on/off devices or activate scenes before starting the updates.
      
        Note => Please be aware that all actions will run right before the update process, which can happens over-night. Take this in account when selecting your actions.
        
        Note => The variable "\{\{ pending_updates \}\}" is available for your automations and contains the list of pending updates.'
      default: []
      selector:
        action: {}
    actions_pos_update:
      name: (optional) Pos-update actions
      description: 'Actions to execute AFTER the update process finishes.

        You can use this to send notifications, turn on/off devices or activate scenes after applying  the updates.
      
        Note => Please be aware that all actions will run right before the update process, which can happens over-night. Take this in account when selecting your actions.

        Note => The variable "\{\{ pending_updates \}\}" is available for your automations and contains the list of pending updates.

        ** IMPORTANT ** => Some updates will automatically restart Home Assistant, causing the automation to interrupt before finishing, preventing the pos-updates actions to be executed. If you have critical actions to run after an update, consider including also in another automation based on Home Assistant start.'
      default: []
      selector:
        action: {}
    pause_entities:
      name: (Optional) Pause update entities
      description: 'You can select one or more entities to pause the updates. If any of the selected entities is "On" or "True" the system won''t be updated on the schedule time.

        You can use this to hold your updates when you have a party at home, or when you are on vacations and don''t want to be concerned about updates on Home Assistant.'
      default: [ ]
      selector:
        entity:
          multiple: true
          domain: [input_boolean, binary_sensor, switch]


mode: restart #if the automation is kept running forever, it will restart on the next schedule.
max_exceeded: warning #It's not expected to have the automation running multiple instances of itself.

variables:
  pending_updates: >-
    {{ states.update | selectattr('state','eq','on') | rejectattr('entity_id', 'in', input_update_exclusions_processed) | list }}
  input_backup_bool: !input backup_bool
  input_schedule_isoweekdays: !input schedule_isoweekdays
  input_schedule_monthday: !input schedule_monthday
  input_update_exclusions: !input update_exclusions
  input_update_exclusions_processed: >-
    {% if input_update_exclusions %}
      {{ input_update_exclusions }}
    {% else %}
      [ ]
    {% endif %}
  input_pause_entities: !input pause_entities
  input_pause_entities_selected: >-
    {% if input_pause_entities %}
      true
    {% else %}
      false
    {% endif %}
  input_pause_entities_processed: >-
    {% if input_pause_entities_selected %}
      {{ input_pause_entities }}
    {% else %}
      [ ]
    {% endif %}

trigger:
  - platform: time
    at: !input schedule_time
    id: Time based

condition:
  - condition: template
    value_template: >-
      {{ (states.update | selectattr('state','eq','on') | rejectattr('entity_id', 'in', input_update_exclusions_processed) | list | count | int(0)) > 0 }}
  - condition: template
    value_template: >-
      {{ (now().day >= ((input_schedule_monthday) | int(0))) }}
  - condition: template
    value_template: >-
      {{ (now().isoweekday() | string in input_schedule_isoweekdays ) }}
  - condition: or
    conditions:
      - condition: template
        value_template: >-
          {{ ( input_pause_entities_selected == false ) }}
      - condition: state
        entity_id: !input pause_entities
        state: 'off'

action:
  ########## Starting ##########
  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: >-
        Variables: 
          input_backup_bool: {{ input_backup_bool }}, 
          input_schedule_isoweekdays: {{ input_schedule_isoweekdays }}, 
          input_schedule_monthday: {{ input_schedule_monthday }}, 
          input_update_exclusions: {{ input_update_exclusions }},
          input_update_exclusions_processed: {{ input_update_exclusions_processed }}
          input_notifications_dest: {{ input_notifications_dest }},
          input_notifications_dest_processed: {{ input_notifications_dest_processed }}

  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      domain: update
      message: A new update is available for Home Assistant.
  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      domain: update
      message:  >-
        List of updates:
        - {{ states.update 
          | selectattr('state','eq','on') 
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='name') | list | join(' 

        - ') }}

  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: Running pre-update actions...

  - choose:
    - conditions:
        - '{{ true }}'
      sequence: !input actions_pre_update
    default: []

  ########## Backup ##########
  - if:
#      - condition: and
#        conditions:
#          - condition: template
#            value_template: >-
#              {{ ( input_backup_bool ) }}
#          - condition: template
#            value_template: >-
#              {{ ((as_timestamp(now()) - as_timestamp(state_attr('sensor.backup_state', 'last_backup'))) > 3600) }}
      - condition: template
        value_template: >-
          {{ ( input_backup_bool ) and ((as_timestamp(now()) - as_timestamp(state_attr('sensor.backup_state', 'last_backup'))) > 7200) }}
##          {{ ( input_backup_bool ) }}
    then:
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Backing up Home Assistant.
      - service: hassio.backup_full
        data:
          compressed: true
      - wait_template: >-
          {{ ((as_timestamp(now()) - as_timestamp(state_attr('sensor.backup_state', 'last_backup'))) <= 7200) }}
        continue_on_timeout: true
        timeout: '3600' #3600
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Backup finished.
    else: []

  ########## Update add-ons (Standard) ##########
  - if: 
      - condition: template
        value_template:  >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | rejectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_operating_system_update','update.home_assistant_supervisor_update','update.home_assistant_google_drive_backup_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) > 0
          }}
    then:
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Updating add-ons (Standard).
      - service: update.install
        data: {}
        target:
          entity_id: >-
            {{ states.update 
            | selectattr('state','eq','on') 
            | rejectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_operating_system_update','update.home_assistant_supervisor_update','update.home_assistant_google_drive_backup_update'])
            | rejectattr('entity_id', 'in', input_update_exclusions_proccessed)
            | map(attribute='entity_id') 
            | list 
            }}
      - wait_template: >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | rejectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_operating_system_update','update.home_assistant_supervisor_update','update.home_assistant_google_drive_backup_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) < 1
          }}
        continue_on_timeout: true
        timeout: '3600' #3600
    else: []

  ########## Update add-ons (Critical) ##########
  - if: 
      - condition: template
        value_template:  >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_google_drive_backup_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) > 0
          }}
    then:
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Updating add-ons (Critical).
      - service: update.install
        data: {}
        target:
          entity_id: >-
            {{ states.update 
            | selectattr('state','eq','on') 
            | selectattr('entity_id', 'in', ['update.home_assistant_google_drive_backup_update'])
            | rejectattr('entity_id', 'in', input_update_exclusions_proccessed)
            | map(attribute='entity_id') 
            | list 
            }}
      - wait_template: >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_google_drive_backup_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) < 1
          }}
        continue_on_timeout: true
        timeout: '3600' #3600
    else: []

  ########## Update core items ##########
  - if: 
      - condition: template
        value_template:  >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_supervisor_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) > 0
          }}
    then:
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Updating core items.
      - service: update.install
        data: {}
        target:
          entity_id: >-
            {{ states.update 
            | selectattr('state','eq','on') 
            | selectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_supervisor_update'])
            | rejectattr('entity_id', 'in', input_update_exclusions_proccessed)
            | map(attribute='entity_id') 
            | list 
            }}
      - wait_template: >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_core_update','update.home_assistant_supervisor_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) < 1
          }}
        continue_on_timeout: true
        timeout: '3600' #3600

  ########## Update OS ##########
  - if: 
      - condition: template
        value_template:  >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_operating_system_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) > 0
          }}
    then:
      - service: logbook.log
        data:
          name: Auto-update
          entity_id: '{{ this.entity_id }}'
          message: Updating OS.
      - service: update.install
        data: {}
        target:
          entity_id: >-
            {{ states.update 
            | selectattr('state','eq','on') 
            | selectattr('entity_id', 'in', ['update.home_assistant_operating_system_update'])
            | rejectattr('entity_id', 'in', input_update_exclusions_proccessed)
            | map(attribute='entity_id') 
            | list 
            }}
      - wait_template: >-
          {{ (states.update 
          | selectattr('state','eq','on') 
          | selectattr('entity_id', 'in', ['update.home_assistant_operating_system_update'])
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='entity_id') 
          | list | count | int(0) ) < 1
          }}
        continue_on_timeout: true
        timeout: '3600' #3600

  ########## Update all remaining items ########## => this chaches up if some update item was left behind
  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: Updating all remaining items (if any).
  - service: update.install
    data: {}
    target:
      entity_id: >-
        {{ states.update 
        | selectattr('state','eq','on') 
        | rejectattr('entity_id', 'in', input_update_exclusions_proccessed)
        | map(attribute='entity_id') 
        | list 
        }}
  - wait_template: >-
      {{ (states.update | selectattr('state','eq','on') | rejectattr('entity_id', 'in', input_update_exclusions_processed) | list | count | int(0)) < 1 }}
    continue_on_timeout: true
    timeout: '3600' #3600

    ########## Ending ##########
  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: All updates done.
  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message:  >-
        Remaining updates:
        - {{ states.update 
          | selectattr('state','eq','on') 
          | rejectattr('entity_id', 'in', input_update_exclusions_processed)
          | map(attribute='name') | list | join(' 

        - ') }}

  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: Running pos-update actions...

  - choose:
    - conditions:
        - '{{ true }}'
      sequence: !input actions_pos_update
    default: []

  - service: logbook.log
    data:
      name: Auto-update
      entity_id: '{{ this.entity_id }}'
      message: Done!

Note: You might want to consider this other Blueprints (which I wasn’t aware of its existence before I published my Blueprint):

2 Likes

Looks super, Will try over the next day

One addition I think is important and super easy to implement, it’s just one input selector and one condition.

A “pause switch”, a simple Boolean to disable. I have a Boolean I use in a number of automations to enable “party mode” for the house, hence the normal night automations do not run.

It makes sense.
In my case I’m running this at 4AM, so the chance of having a party is really low (I leave in an apartment, so the neighbors will complain before the update starts :joy:), but still makes sense (I probably don’t wanna it running while I’m travelling, for example).

I will take a look and come back soon.

You can just turn off the automation. Automations are either on or off and when they’re off they do nothing. You can toggle them in the UI or using the automation.turn_on and automation.turn_off services. Not really any need for an “off entity”, all automations already have one.

I notice you seem to be updating core, operating system and supervisor first? This seems odd for a few reasons:

  1. it’s really unlikely this will ever update supervisor running once a day. Supervisor already auto updates.
  2. when the automation updates core or operating system then the automation will immediately stop. It won’t proceed to the remaining steps so nothing else will be updated that day. Seems like you have this in the wrong order, update the addons first and core/os last.
  3. you seem to be assuming you know the entity ids of particular update entities like the one for core and os. Users can change the entity ID for these from the UI. The one for core won’t always be update.home_assistant_core_update that’s just the default. See here for how I accounted for this in the update notifications blueprint you linked.

Also your blueprint is very likely to attempt to install multiple addon updates at the same time. Have you tested that? Because I’m pretty sure that doesn’t work.

Hi @CentralCommand, thanks for your comments.

Nope, it’s the opposite. I update everything, except by those core items (and I’ve also left the Google Drive backup for later). Note I’ve used rejectattr instead of selectattr.

It’s a good point about users renaming the entities names… I will take a look on that. Anyways, it has a catch all in the end, so it will do those anyways.
I want to keep it simple. :wink:

If you don’t mind, I will copy that part of your code, ok? :grimacing:

By the way, what is “Send to HA” in your Blueprint?

1 Like

Hi Mike, I know this is possible, I even started using this, having one script disabling all the automations when in party mode, and enable again.
The problem is I forget to update the scripts when creating new automations, a human problem. It is easier to remember (at least for me) to include the condition in the automations.

Make a persistent notification in addition to a mobile app one. That way you can see the notification in HA as well. Also makes it usable for people who don’t use the mobile app since everyone can send persistent notifications.

Sure np, feel free :slightly_smiling_face:

1 Like

Ah yep I missed that. Yea that makes sense then

1 Like

I’ve finally spent some time to implement the “party mode” support. Will just test for a week (as I will be on a hike the entirely next week) and publish here if everything goes fine. Is that OK with you, @khvej8?

1 Like

Super. Looking forward

1 Like

I had an OS upgrade and core update pending waiting to test the automation I was writing for auto upgrades but after I found your template I don’t have to bother :).
Anyway I think it will be good if you handle this edge case of OS + core/supervisor update. They should be triggered in sequence and not simultaneously. I mean my upgrade succeeded but in general triggering OS + Core update is risky in my opinion.

1 Like

I will do that.
:wink:

I’ve just updated the Blueprint with the “Pause” option.

Done!
Please let me know i you have any issue still.

Will install tomorrow and let you know if it works.
Regarding, if the upgrade works, it might take some time.I will not go for first monthly version, and I will pause due to vacation, maybe I get one update in july. No updates when not close to home.

However I’m fully in for this and will get back with any problems or possibilities for improvements.

1 Like

Installed perfectly. No issues at all.

1 Like

One thing I’d like to see is a time-based delay for updates. For example: wait 2 days before updating.
I want this automatic, but to make it so, one must reduce the risk.

A delay would give the HA team time to fix & release any major bugs before they are applied. THis could of course be set to “0”.

2 Likes

Hi Edward, it is installed with no problems, and the blueprint have made the first upgrade today. From 2022.07.1 to 2202.07.2. No problems at all. Will let it run from now on and see how it behaves during the month. Will let you know.

1 Like