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)