~/jackwent.co.uk
jack@jackwent:~$ cat esphome-updates.yaml

Hands-Off ESPHome Firmware Updates

published 7 October 20262 min read

I’ve got a dozen ESPHome devices around the house: the fish feeder, the fish tank lights and temperature sensors, plant sensors, smart plugs, an RFID reader and a Bluetooth proxy in most rooms. ESPHome is brilliant, but every new release means a little “update available” badge on every single device, and updating them by hand is a chore.

So I don’t. Two automations do it for me overnight.

Step one: update ESPHome itself

The ESPHome Device Builder add-on has its own update entity in Home Assistant. When a new version comes out, this automation installs it (with a backup first, just in case), waits for it to finish, gives it five minutes to settle, then kicks off the device updates straight away.

alias: Update ESPHome
mode: single
triggers:
  - trigger: state
    entity_id: update.esphome_update
    to: "on"
actions:
  - action: update.install
    target:
      entity_id: update.esphome_update
    data:
      backup: true
  - wait_for_trigger:
      - trigger: state
        entity_id: update.esphome_update
        to: "off"
    timeout: "00:15:00"
    continue_on_timeout: true
  - delay:
      minutes: 5
  - action: automation.trigger
    target:
      entity_id: automation.esp_devices_firmware_updates
    data:
      skip_condition: true

Step two: update the devices, one at a time

Each ESPHome device has its own firmware update entity, which turns on when the device is running older firmware than the Device Builder. The second automation runs at 1am every night and works through them in turn.

The important bit is one at a time. Updating a device means compiling its firmware first, which is heavy work for a small box like the Home Assistant Yellow. Start ten of those at once and everything slows to a crawl. So for each device, the automation starts the update and then waits until the update entity turns off before moving on. If something hangs, a twenty minute timeout makes sure the rest still get done.

alias: ESP devices firmware updates
mode: single
triggers:
  - trigger: time
    at: "01:00:00"
actions:
  - repeat:
      for_each:
        - update.fish_feeder_firmware
        - update.fish_tank_lights_firmware
        - update.fish_tank_temperature_firmware
        - update.plant_sensor_firmware
        - update.office_bt_proxy_firmware
        - update.living_room_bt_proxy_firmware
        - update.rfid_reader_firmware
      sequence:
        - if:
            - condition: template
              value_template: "{{ is_state(repeat.item, 'on') }}"
          then:
            - action: update.install
              target:
                entity_id: "{{ repeat.item }}"
            - wait_template: "{{ is_state(repeat.item, 'off') }}"
              timeout: "00:20:00"
              continue_on_timeout: true

Why a loop is better than copy and paste

My original version didn’t use repeat. It had a separate choose block for every device, each one an exact copy of the last with the entity names changed. It worked, but when I went through it while writing this post I found that one block installed the update on one device and then waited for a different device that doesn’t exist any more. Nothing broke. It just sat there for twenty minutes every time that device had an update, before giving up and carrying on.

That’s the trouble with copy and paste: every copy is another chance to forget to change something. With for_each each device is one line in a list, and the install and the wait always use the same entity because they both read repeat.item.

A couple of tips

  • Only list devices you’re happy to update unattended. If something critical runs on an ESP and you want to watch it come back, leave it out and update it by hand.
  • Pick a quiet time. Devices reboot after flashing, so a 1am run means the fish tank lights don’t flicker while anyone’s watching. The fish don’t seem to mind.
  • Look for stragglers now and then. If a device fails to update, its update entity just stays on and the automation tries again the next night. A quick look at Settings > Updates every so often will spot anything that keeps failing.

Between this and my server update alerts, the only updates I deal with by hand now are the ones I actually want to watch.

[jackwent]