How To: Automation For OTA Zigbee/Zwave Device Firmware Updates

Hey guys,

I’m trying to create an Automation that will automatically upgrade the firmware on my Zigbee devices (light switches, bulbs, etc). (I think this might actually work for Zwave as well)

I want the automation to check for updates periodically, then queue the devices that have firmware updates available and upgrade them one at a time.

Why?

I have over 200 devices on my Zigbee Network and almost all of them have an update available.

Each of these updates takes anywhere from 30mins to an hour. And if more than of them is trying to upgrade at the same time, it takes even longer and can actually crash the controller.

Currently for both Zwave and Zigbee I’m using MQTT, Zwave2MQTT and Zigbee2MQTT respectively.

Both of these support OTA Updates for device connected to them

I know this is possible, just not with my noob skills.

There are three projects that have the “pieces”, I think.

  1. @CyanAutomation 's Automation for Updating all Shelly device to the latest firmware using MQTT

In spirit this does what I need, except it only applies to Shelly and it doesn’t queue them one device at a time.

  1. @Blacky 's Low Battery Notifications and Actions
    This script looks for all the devices that match a certain criteria and groups them for specific action. In this case it’s about batteries, but I’m assuming the same could be done for if the device has an upgrade available.

  2. Fabian Jackl’s Zigbee2MQTT OTA Updater Script

This script does exactly what I want, it’s just not a home assistant automation.

Has anyone done something like this already? Anyone care to help?

This could be dangerous. HA does not vet firmware. Just because there is ‘newer’ firmware available does not mean it is ‘better’. Jasco has actually been removing functionality to make support easier. I think you’d still want to manually review/approve the updates. The rest could be automated.

I concur with @nmpu , if a zigbee device (I would apply this same philosophy most any home automation firmwares and subsystems) is ‘working’ don’t screw with it and most definitely not in a automation that runs without you first reviewing, testing and watching others experiences.

IMHO, my experiences speaking here.

@nmpu @dproffer thanks, but I’m not asking if I should do it. I’m asking how I can do it. I appreciate y’all’s opinion, but that’s not actually helpful in solving the problem.

To each their own :slight_smile:

When you have over 100 Inovelli switches and over 100 Hue bulbs that all need to be updated, manually clicking a button once every hour is a huge pain and not a scalable solution.

3 Likes

I’m not saying I’m going to run the automation all the time without any oversight.

Here’s an example…

Let’s say you have over 100 hue bulbs that all have the older firmware that have the low glow when off bug.

And you’ve already tested the new firmware and everything is fine…

How can you queue all of those devices to upgrade their firmware one by one without you manually pressing the button once an hour?

Ok ok soapbox off :wink:

Not sure where you are with coding. However, it seems like interacting directly with Zigbee2MQTT via MQTT commands would be the best way to do this. I think Home Assistant’s automations are a bit weak to get a robust solution working. Zigbee2MQTT can be controlled and queried pretty extensively via MQTT. It’s just a bit of work to poke around the documentation and browse MQTT to figure out the interactions.

Have a docker python or bash or ? script (any language that can interact with MQTT should work) running nonstop that would grabs all visible devices say at 1 am, then steps thru each to check for an update. Then fire off the update for one device, wait for success or failure, report and the move to next device. Or you could just have it do the check for updates and then publish this to you. Then you would ‘authorize’ the update for one or several or a group, then next cycle it would do the update.

FYI, from the docs:

An update typically takes +- 10 minutes. While a device is updating a lot of
traffic is generated on the network, therefore it is not recommend to 
execute multiple updates at the same time.

Good hunting!

Same exact scenario here - close to 200 z2m devices, 90% of which are Inovelli switches and Hue bulbs and the constant alerting for available updates and then working through the list one-by-one is getting old :frowning: Really wish there was a solution here. Or at the absolute very least a way to disable the update available notification in the lower left corner.

EDIT: Just realized there is an automatic update check disabler…still wish there were a better option but I guess that might end up being my solution for the time being.

I was just thinking about this, having seen that several of my devices wanted updating.
HA is aware of when updates are available, so I’m thinking you could do something like:

Have an automation with a state trigger that looks for the update.devicename sensor changing to on, indicating that an update is available.

Then, this could kick-off the Z2M update by publishing the ota_update to Z2M via MQTT.

Then, have a wait condition to wait for the attribute in_progress to change from true to false - i.e. wait for the update to complete.

Then allow the automation to end.

To prevent multiple updates running concurrently, set the automation to Queued mode.

Probably the main question for me is how to select the many zigbee devices in the trigger, without looking at all devices, zigbee or not.

But, I think this should be doable…

Also, it would be easily doable to add some kind of confirmation prompt to this - send a notification to a phone asking if it’s ok to update, and then queue the update if confirmed.

I’ve come up with the following automation.

This runs at 19:30 each day and checks for any update.* entities with state=on (devices needing an update).

For each device needing an update it publishes an MQTT message to Z2M to trigger an OTA update, waits for the update to start, then waits for the update to finish before going on to the next device.

I know there may be other devices with entities of update.*, but Z2M simply ignores anything where there isn’t a matching device name or where an update isn’t required, so it works ok.

Of course, this could be run on any other time pattern, etc.

alias: Check for and trigger Z2M OTA updates
description: >-
  Checks for and trigger Z2M OTA updates.
trigger:
  - platform: time_pattern
    minutes: "30"
    hours: "19"
condition: []
action:
  - variables:
      update_entities: >
        {{ states.update | selectattr('state', 'eq', 'on') |
        map(attribute='entity_id') | list }}
    alias: Get list of Z2M devices needing an update
  - alias: Run OTA update on devices with updates
    repeat:
      sequence:
        - variables:
            triggered_entity: "{{ repeat.item }}"
          alias: Get entity_id of this specific device
        - service: mqtt.publish
          metadata: {}
          data:
            qos: 0
            retain: false
            topic: zigbee2mqtt/bridge/ota_update/update
            payload: "{{ states[triggered_entity].attributes.friendly_name }}"
          alias: Send MQTT message to Z2M to request update
        - wait_template: "{{ is_state_attr(triggered_entity, 'in_progress', true) }}"
          continue_on_timeout: true
          timeout: "00:01:00"
          alias: Wait a minute for the Z2M update to start
        - wait_template: "{{ is_state_attr(triggered_entity, 'in_progress', false) }}"
          continue_on_timeout: true
          timeout: "01:00:00"
          alias: Wait up to 1 hour for the OTA upgrade to finish
      for_each: "{{ update_entities }}"
mode: single
8 Likes

Thanks for this @DerekO!

Just a note for others that might try to use this.

in the settings of your Z2M instance, you HAVE TO HAVE “Legacy API” &/or “Legacy availability payload” enabled.

My downstairs Z2M instance didn’t have this setting enabled and was unable to update devices via this automation until I enabled them.

Awesome, thanks for this!!

BTW: For future readers, swap zigbee2mqtt/bridge/ota_update/update with zigbee2mqtt/bridge/request/device/ota_update/update so you don’t need to have the legacy z2m api enabled.

5 Likes

This is super nice. This morning it showed 82 devices needing a upgrade, all Hue :frowning: A lot of work. This automation is saving me a lot of time.

I have created a slightly modified version, as I have two Z2M networks, of course with different zigbee topic. The above will only work for one.

Be aware I do not have a “trigger”, as I would like to be in control of when to run.

For inspiration, if others are in a similar situation.

alias: Checks for and trigger Z2M OTA updatee, new
description: Checks for and trigger Z2M OTA updates.
trigger: []
condition: []
action:
  - variables:
      update_entities: |
        {{ states.update | selectattr('state', 'eq', 'on') |
           selectattr('attributes.device_class', 'eq', 'firmware') |
           map(attribute='entity_id') | list  }}
    alias: Get list of Z2M devices needing an update
  - alias: Run OTA update on devices with updates
    repeat:
      sequence:
        - variables:
            triggered_entity: "{{ repeat.item }}"
          alias: Get entity_id of this specific device
        - action: update.install
          target:
            entity_id:
              - "{{ repeat.item }}"
        - wait_template: "{{ is_state_attr(triggered_entity, 'in_progress', true) }}"
          continue_on_timeout: true
          timeout: "00:01:00"
          alias: Wait a minute for the Z2M update to start
        - wait_template: "{{ is_state_attr(triggered_entity, 'in_progress', false) }}"
          continue_on_timeout: true
          timeout: "02:00:00"
          alias: Wait up to 1 hour for the OTA upgrade to finish
      for_each: "{{ update_entities }}"
mode: single

Great topic! Thanks @DerekO and @promptly3 !

The current template for update_entities is not specific to MQTT entities so it might trigger updates of unrelated components, including HA itself (It’s improved by @khvej8’s suggestion of using selectattr('attributes.device_class', 'eq', 'firmware') but I wanted to be even more precise and don’t update other lights like Shelly’s).

I’ve updated the list of targeted entities so it includes only MQTT light entities (I did not find how to target only Z2M entities easily), and I also have a list of excluded entities for the ones you really don’t want to update automatically (or that fail to update to some reason).

  - variables:
      update_entities: >
        {% set exclude_entities = ['update.garage_zlinky'] %}
        {% set mqtt_update_entities = states.update | selectattr('entity_id', 'in', integration_entities('mqtt')) | selectattr('state', 'eq', 'on') | map(attribute='entity_id') | list %}
        {% set light_entities = states.light | map(attribute='entity_id') | map('replace', 'light.', 'update.') | list %}
        
        {{ mqtt_update_entities 
            | select('in', light_entities) 
            | reject('in', exclude_entities) 
            | list }}
2 Likes

This is a great improvement - thank you.

I noticed that the only other update entities I have for the MQTT integration are for What’s Up Docker, which in my case all start with “update.wud_container_docker_”, so I’ve added the following line to exclude all of them:

    | reject('match', 'update.wud_container_docker_')