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.
In spirit this does what I need, except it only applies to Shelly and it doesn’t queue them one device at a time.
@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.
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.
@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
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.
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.
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 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.
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
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.
This is super nice. This morning it showed 82 devices needing a upgrade, all Hue 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
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 }}
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: