I learned something new in a Saturday morning…
Thanks for sharing!!
Updating the esphome devices one-by-one sequentially is considered, as this script runs every minute from 3.00-3.59am, on the condition that none of the esphome nodes in the list has in_progress=true
, and passes only the first node that has update.state=on
to the update.install
service .
- service: update.install
target:
entity_id: "{{ (states.update | selectattr('entity_id', 'match', 'update\.node\d+_firmware')
| selectattr('state', 'eq', 'on') | first).entity_id }}"
Unless you meant calling up the entire states.update
object and them filtering them poses a huge cpu drain, then yea that I’m unaware of… Is processing a list of entities really that resource intensive?
Thanks for the script, @loongyh. I am getting an error when trying to edit it in the File Editor - but can’t figure out what is wrong.
The code is copy / pasted from yours so I’m pretty sure it’s not a typing error and as I am a novice at this I don’t know how to fix it.
Does anyone have an idea, please?
I just stumbled upon this myself today and decided to go a slightly different way:
automation:
- alias: Auto-update ESP devices
trigger:
- platform: time_pattern
hours: "3"
minutes: "*"
action:
- variables:
outdated_esp_items: >-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
next_to_update: "{{ outdated_esp_items | first }}"
- condition:
- "{{ outdated_esp_items | count > 0 }}"
- service: update.install
target:
entity_id: "{{ next_to_update }}"
mode: single
This works great! Thank you @bjeanes !
The naming convention I have for my ESPHome nodes uses prefixes (e.g. light_, switch_, sensor_) - and I’d like to be able to run this for the various groups separately (for example, run lights_ during the day when the flash as they restart won’t matter).
Have you any thoughts for how that functionality to this, please?
There are a few ways one might achieve this. If I were doing it, I’d probably do something like this, using trigger variables:
automation:
- alias: Auto-update ESP devices
trigger:
- platform: time_pattern
hours: 3
minutes: "*"
variables:
prefix: sensor_
- platform: time_pattern
hours: 12
minutes: "*"
variables:
prefix: light_
# etc ...
action:
- variables:
outdated_esp_items: >-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| selectattr('entity_id', 'match', prefix)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
next_to_update: "{{ outdated_esp_items | first }}"
- condition:
- "{{ outdated_esp_items | count > 0 }}"
- service: update.install
target:
entity_id: "{{ next_to_update }}"
mode: single
In other words, each trigger can introduce its own values for variables, so we create a trigger for each entity_id
prefix, and add a filter to the outdated_esp_items
template to use the prefix that the trigger sets.
Note: I haven’t tested this; I wrote it directly into here, so it might have some typos or might be slightly off. But I suggest trying to read through Understanding Automations - Home Assistant, including every other nav item under Automations in the Topics sidebar.
Thank you so much - I will try that, and go and do some reading!
I double checked with the last update:
The esphome-addon will compile and update the devices one-by-one after the update-service ist called for the complete list of outdated esphomes.
So it should be fine to call the update once a night:
automation:
- alias: Auto-update ESP devices
trigger:
- platform: time_pattern
hours: "3"
minutes: "15"
action:
- variables:
outdated_esp_items: |-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.device_class', 'eq', 'firmware')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
- condition: template
value_template: "{{ outdated_esp_items | count > 0 }}"
- service: update.install
target:
entity_id: "{{ outdated_esp_items }}"
mode: single
Edit: Better condition that shows up in the gui.
Edit 2: Select only “firmware” entities and leave the addon update to the addon manager
Edit 3: Delete duplicate condition
Thanks for sharing, this was driving me crazy and saved me so much time!
… although the compile itself seems to run at the same time, I have 6 cc1 processes and 6 ESPHome nodes?
Running with Frigate, which is pretty hungry anyway, doesn’t leave me much/any headroom in terms of processing.
Will have to look into something that does sequential compile, I think, for me personally.
EDIT: Untested, but perhaps something like this
Create a script:
update_esphome_sequentially:
for_each: >-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.device_class', 'eq', 'firmware')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
sequence:
- service: update.install
target:
entity_id: "{{ repeat.item }}"
Call the script in the automation at the end, instead of the update.install?
EDIT 2: Oh maybe not, needs some tweaking:
Script with object id 'update_esphome_sequentially' could not be validated and has been disabled: extra keys not allowed
EDIT 3: Had a formatting error, the below validates and loads:
update_esphome_sequentially:
sequence:
- repeat:
for_each: >-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.device_class', 'eq', 'firmware')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
sequence:
- service: update.install
target:
entity_id: "{{ repeat.item }}"
Whether it will do what I want, remains to be seen. And then the automation:
- id: 'Auto-update ESP devices'
alias: Auto-update ESP devices
trigger:
- platform: time_pattern
hours: "3"
minutes: "15"
action:
- variables:
outdated_esp_items: |-
{{ states.update
| selectattr('state', 'eq', 'on')
| selectattr('attributes.title', 'match', 'ESPHome')
| selectattr('attributes.device_class', 'eq', 'firmware')
| selectattr('attributes.auto_update', 'eq', false)
| selectattr('attributes.in_progress', 'eq', false)
| sort(attribute='attributes.installed_version')
| map(attribute='entity_id')
| list }}
- condition: template
value_template: "{{ outdated_esp_items | count > 0 }}"
- service: script.update_esphome_sequentially
EDIT 4: Probably not needed after all, see below…
interesting… my observation comes from the esphome-addon’s logfile where every update processes seemed to be started sequentially node by node.
Could you cross-check (i) how your addon log behaves and (ii) how many cc1 processes will be created with a single update?
… Looks like you’re right! When doing a clean build re-install as a test, anyway. Should have checked this to start with! Got carried away.
I assume it’s spawning 1 per core (4 core box) that do the heavy lifting.
Oh well, learnt something about for_each and repeat in scripts
Can you share your script.notify please? Thanks!
I guess this script is sending data to script.notify
That’s a very convoluted script that handles all notification channels, etc. I’m not proud of it.
What are you looking for?
For your take on a general notification script .) I understand its purpose from the code you use but never thought to write my own as I am not a coder, so what I can with code mostly is to alter examples for my own purposes. So if you could share yours, I would be grateful
Thank you! This is wonderful!
I’m using the Home Assistant Yellow and found that the updates would time out due to lack of resources so I implemented a stop add-ons | Update esphome | restart add-ons workflow:
Automation:
alias: Update- ESPHome Update All Devices
description: ""
trigger:
- platform: template
value_template: >-
{{ integration_entities('esphome') | select('match', '^update.') |
select('is_state', 'on') | list | count > 0 }}
condition: []
action:
- service: script.esphome_update_all_esphome_devices
metadata: {}
data: {}
mode: single
Script to update:
alias: ESPHome- Update All ESPHome Devices
sequence:
- service: script.add_ons_stop
data: {}
- repeat:
sequence:
- service: update.install
data: {}
target:
entity_id: "{{ repeat.item }}"
- wait_template: "{{ is_state(repeat.item, 'off') }}"
continue_on_timeout: true
for_each: >-
{{ states.update | selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in',integration_entities('esphome')) | list }}
- service: script.add_ons_start
data: {}
mode: single
Script to stop:
alias: Add-Ons_Stop
sequence:
- service: hassio.addon_stop
data:
addon: core_whisper
- service: hassio.addon_stop
data:
addon: d63406df_acurite2mqtt
- service: hassio.addon_stop
data:
addon: a0d7b954_ssh
- service: hassio.addon_stop
data:
addon: a0d7b954_airsonos
- service: hassio.addon_stop
data:
addon: a0d7b954_appdaemon
- service: hassio.addon_stop
data:
addon: a0d7b954_chrony
- service: hassio.addon_stop
data:
addon: core_configurator
- service: hassio.addon_stop
data:
addon: cebe7a76_hassio_google_drive_backup
- service: hassio.addon_stop
data:
addon: core_mosquitto
- service: hassio.addon_stop
data:
addon: 2ad4c73a_mqtt-explorer
- service: hassio.addon_stop
data:
addon: a0d7b954_nut
- service: hassio.addon_stop
data:
addon: core_openwakeword
- service: hassio.addon_stop
data:
addon: core_piper
- service: hassio.addon_stop
data:
addon: 03cabcc9_ring_mqtt
- service: hassio.addon_stop
data:
addon: 15d21743_samba_backup
- service: hassio.addon_stop
data:
addon: core_samba
- service: hassio.addon_stop
data:
addon: a0d7b954_vscode
- service: hassio.addon_stop
data:
addon: core_whisper
mode: single
Script to start:
alias: Add_Ons_Start
sequence:
- service: hassio.addon_start
data:
addon: core_whisper
- service: hassio.addon_start
data:
addon: d63406df_acurite2mqtt
- service: hassio.addon_start
data:
addon: a0d7b954_ssh
- service: hassio.addon_start
data:
addon: a0d7b954_airsonos
- service: hassio.addon_start
data:
addon: a0d7b954_appdaemon
- service: hassio.addon_start
data:
addon: a0d7b954_chrony
- service: hassio.addon_start
data:
addon: core_configurator
- service: hassio.addon_start
data:
addon: cebe7a76_hassio_google_drive_backup
- service: hassio.addon_start
data:
addon: core_mosquitto
- service: hassio.addon_start
data:
addon: 2ad4c73a_mqtt-explorer
- service: hassio.addon_start
data:
addon: a0d7b954_nut
- service: hassio.addon_start
data:
addon: core_openwakeword
- service: hassio.addon_start
data:
addon: core_piper
- service: hassio.addon_start
data:
addon: 03cabcc9_ring_mqtt
- service: hassio.addon_start
data:
addon: 15d21743_samba_backup
- service: hassio.addon_start
data:
addon: core_samba
- service: hassio.addon_start
data:
addon: a0d7b954_vscode
mode: single
Would love to clean up the list of add-ons to stop/start if possible but so far, this has gotten me past the timeouts while attempting to update. I’ve also added the stop/start scripts to an update-dashboard so I can call them manually in the event I want to apply a custom firmware to a device (such as having the AtomEcho output its audio to the nearest Sonos speaker)
Hi all, I have found this automation super helpful! I updated the automation from the original post to work purely based on templates with no hard coded entity IDs! Thought technically it uses device id for notifications still. I also moved the condition from the top level (blocking the finished notification) to the choose stage.
Hopefully you guys find it useful!
alias: "Schedule: Automatically update ESPHome Devices"
description: >-
Automatically update ESPHome Devices at midnight, and notify when they are
updated.
trigger:
- platform: time
at: "00:00:00"
id: update_check
- alias: When all devices are updated
platform: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities == 0 }}
- platform: event
event_type: timer.finished
event_data:
entity_id: timer.esphome_updates_timer
id: esphome_timer_updates_finished
condition: []
action:
- choose:
- conditions:
- condition: trigger
id: update_check
- condition: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities > 0 }}
alias: Confirm something needs to be updated still
sequence:
- service: timer.start
data:
duration: 0
target:
entity_id: timer.esphome_updates_timer
- service: notify.mobile_app_alexanders_iphone
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%}
Your ESPHome devices will now be updated from {{
state_attr(entity, "installed_version") }} to {{
state_attr(entity, "latest_version") }}.
- alias: Update all ESPHome devices in sequence
repeat:
for_each: |-
{{ states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list
}}
sequence:
- service: update.install
target:
entity_id: "{{ repeat.item }}"
- wait_template: "{{ is_state(repeat.item, 'off') }}"
- conditions:
- condition: trigger
id: up-to-date
- condition: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities == 0 }}
alias: Confirm all are updated
sequence:
- service: timer.cancel
data: {}
target:
entity_id: timer.esphome_updates_timer
- service: notify.mobile_app_alexanders_iphone
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%}
Your ESPHome devices are now all updated to {{
state_attr(entity, "latest_version") }}.
- conditions:
- condition: trigger
id: esphome_timer_updates_finished
sequence:
- service: notify.mobile_app_alexanders_iphone
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%} There was a problem updating to {{ state_attr(entity,
"latest_version") }}. Please check your ESPHome Addon.
mode: single
awesome script and works fine, thanks.
I adjusted it a bit so it will automatically update all my ESP devices as soon as ESPHome has been upgraded to new version instead of check daily at 00:00.
alias: ESPHome Update Checker
description: >-
Automatically update ESPHome Devices after ESPHome upgrade, and notify when they are
updated.
trigger:
- platform: template
value_template: >-
{{ integration_entities('esphome') | select('match', '^update.') |
select('is_state', 'on') | list | count > 0 }}
id: update_check
- alias: When all devices are updated
platform: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities == 0 }}
- platform: event
event_type: timer.finished
event_data:
entity_id: timer.esphome_updates_timer
id: esphome_timer_updates_finished
condition: []
action:
- choose:
- conditions:
- condition: trigger
id: update_check
- condition: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities > 0 }}
alias: Confirm something needs to be updated still
sequence:
- service: timer.start
data:
duration: 0
target:
entity_id: timer.esphome_updates_timer
- service: notify.mobile_app_<change to your device>
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%}
Your ESPHome devices will now be updated from {{
state_attr(entity, "installed_version") }} to {{
state_attr(entity, "latest_version") }}.
- alias: Update all ESPHome devices in sequence
repeat:
for_each: |-
{{ states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list
}}
sequence:
- service: update.install
target:
entity_id: "{{ repeat.item }}"
- wait_template: "{{ is_state(repeat.item, 'off') }}"
- conditions:
- condition: trigger
id: up-to-date
- condition: template
value_template: |-
{% set entities = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
list | count
%}
{{ entities == 0 }}
alias: Confirm all are updated
sequence:
- service: timer.cancel
data: {}
target:
entity_id: timer.esphome_updates_timer
- service: notify.mobile_app_<change to your device>
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%}
Your ESPHome devices are now all updated to {{
state_attr(entity, "latest_version") }}.
- conditions:
- condition: trigger
id: esphome_timer_updates_finished
sequence:
- service: notify.mobile_app_<change to your device>
data:
title: ESPHome Devices
message: >-
{% set entity = states.update |
selectattr('state', 'eq', 'on') |
map(attribute='entity_id') |
select('in', integration_entities('esphome')) |
first
%} There was a problem updating to {{ state_attr(entity,
"latest_version") }}. Please check your ESPHome Addon.
mode: single