Great work! However, I can’t get the esphome_version-sensor to work.
I’ve copied the command_line sensor to my config file, but it never gets a sensor state.
Yes, I just figured out what happened
The current releases are the following:
home-assistant:/config# curl -s -X GET https://hub.docker.com/v2/namespaces/esphome/repositories/esphome/tags | jq -r '.results|.[]|.name'
latest
2024.1.0-dev20240118
dev
2024.1.0-dev20240117
2023.12.7
2023.12
beta
stable
2024.1.0-dev20240116
2023.12.6
As the command line tries to find the latest stable release of this year - and there is no release for this year yet - it fails and returns nothing.
The fix is to replace the command line:
curl -s -X GET https://hub.docker.com/v2/namespaces/esphome/repositories/esphome/tags | jq -r '.results|.[]|.name' | egrep -i "^$(date +'%Y')\.[0-9]{1,2}\.[0-9]{1,2}$" | sort -rn | head -1
with the following command line:
curl -s -X GET https://hub.docker.com/v2/namespaces/esphome/repositories/esphome/tags | jq -r '.results|.[]|.name' | egrep -i "^[0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}$" | sort -rn | head -1
as my ESPHome-version sensor showed ‘unknown’, I started research again and figured out, by default docker just returned the latest 10 tags - which is not sufficient for now as there is no stable release of ESPHome within the lastest 10 tags.
Therefore, I added the flag for page_size to the request.
If you notice the same issues on your end, please replace your command line by this one:
curl -s -X GET https://hub.docker.com/v2/namespaces/esphome/repositories/esphome/tags?page_size=50 | jq -r '.results|.[]|.name' | egrep -i "^[0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}$" | sort -rn | head -1
I’m having a problem with the template script. My devices are at the current version, however the template sensor is showing them as being out of date. Any ideas?
do you have this issue just sometimes or more often?
What would be the output if you copy the following template to the developer tools in the template section?
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_name = device_attr(device_id(entity), 'name') -%}
{%- if 'version' in entity and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in states(entity) -%}
{%- set ns.outdated = ns.outdated + [ device_name ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
Okay, then let’s start debugging. Could you please add the following snippet to the template developer tools to check which condition fails? After that we should be able to find the reason for that
{{- "ESPHome-Version-Sensor: '" + states('sensor.esphome_version') + "'\n" }}
{{- "----------------------\n" }}
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_name = device_attr(device_id(entity), 'name') -%}
{%- if 'version' in entity and states(entity) != "unavailable" -%}
{{- "Valid version entity found: '" + entity + "'\n" }}
{%- if not states('sensor.esphome_version') in states(entity) -%}
{{- "OUTDATED - Version entity ('" + entity + "') has value: '" + states(entity) + "'\n" }}
{%- set ns.outdated = ns.outdated + [ device_name ] -%}
{%- else %}
{{- "UP2DATE - Version entity ('" + entity + "') has value: '" + states(entity) + "'\n" }}
{%- endif -%}
{%- else %}
{{- "Skipping entity '" + entity + "'\n" }}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
Okay, now it’s clear, why this happens:
The template takes the current ESPHome-version (which is 2024.2.2 in this case) and checks whether this text can be found within the values for the version-entities.
It works e.g. for sensor.ratgdov25i_fada22_firmware_version as there the value 2024.2.2 Mar 9 2024, 11:43:55 contains this text. The version sensor.libretiny_version has the value v1.4.1 on generic-bk7231t-qfn32-tuya, compiled at Mar 9 2024 11:39:32, GCC 10.3.1 (-O1) which does not match the ESPHome-version-scheme (even if it might be managed with ESPHome).
If you can get the latest version of those devices as well, you can follow the same approach as for ESPHome but as long as the version numbers don’t match, the entities/devices will be reported as outdated.
Otherwise you can exclude the entities from the version check:
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = [], excluded = ['Your-Device-Name-Goes-Here', 'Second-Device-Name']) -%}
{%- for entity in entities -%}
{%- set device_name = device_attr(device_id(entity), 'name') -%}
{%- if not device_name in ns.excluded -%}
{%- if 'version' in entity and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in states(entity) -%}
{%- set ns.outdated = ns.outdated + [ device_name ] -%}
{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
Thanks for your help with this. I updated the code you sent to exclude by entity instead of by device, this way the code will still check the device for the correct Esphome version. Code is below for reference if anyone else wants it.
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = [], excluded = ['sensor.living_room_dimmer_libretiny_version', 'sensor.libretiny_version_2', 'sensor.libretiny_version']) -%}
{%- for entity in entities -%}
{%- set device_name = device_attr(device_id(entity), 'name') -%}
{%- if not entity in ns.excluded -%}
{%- if 'version' in entity and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in states(entity) -%}
{%- set ns.outdated = ns.outdated + [ device_name ] -%}
{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
Would it be worth retooling to use the existing sw_version in the esphome integration rather than a new sensor? A quick change to use it instead below. Note it still goes through every sensor rather than just the device (too late at night to figure that one out yet).
sensor:
- platform: template
sensors:
# Sensor for ESPHome updates
calculated_esphome_updates:
friendly_name: ESPHome Updates
attribute_templates:
outdated_devices: >-
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr(device_id(entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in device_version -%}
{%- set ns.outdated = ns.outdated + [ device_version ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
outdated_devices_number: >-
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr(device_id(entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in device_version -%}
{%- set ns.outdated = ns.outdated + [ device_version ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated | count | int -}}
icon_template: >-
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(up2date = [], outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr(device_id(entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if states('sensor.esphome_version') in device_version -%}
{%- set ns.up2date = ns.up2date + [ entity ] -%}
{%- else -%}
{%- set ns.outdated = ns.outdated + [ entity ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- if ns.outdated | count >= 1 -%}
mdi:package-up
{%- elif ns.up2date | count >= 1 -%}
mdi:package-check
{%- else -%}
mdi:help-box
{%- endif -%}
value_template: >-
{%- set entities = integration_entities('esphome') -%}
{%- set ns = namespace(up2date = [], outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr(device_id(entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if states('sensor.esphome_version') in device_version -%}
{%- set ns.up2date = ns.up2date + [ entity ] -%}
{%- else -%}
{%- set ns.outdated = ns.outdated + [ entity ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- if ns.outdated | count == 0 and ns.up2date | count >= 1 -%}
No update available
{%- elif ns.outdated | count == 1 -%}
Update available for one device
{%- elif ns.outdated | count >= 2 -%}
{{- "Updates available for " + ns.outdated | count | string + " devices"-}}
{%- else -%}
No ESPHome-device found
{%- endif -%}
This will make it a single entity per device, given there is no preset entity on ESPhome devices that I am aware of. I would image there is a cleaner way.
sensor:
- platform: template
sensors:
# Sensor for ESPHome updates
calculated_esphome_updates:
friendly_name: ESPHome Updates
attribute_templates:
outdated_devices: >-
{%- set entities = integration_entities('esphome') | map('device_id') | unique | list -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr((entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in device_version -%}
{%- set ns.outdated = ns.outdated + [ device_version ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated -}}
outdated_devices_number: >-
{%- set entities = integration_entities('esphome') | map('device_id') | unique | list -%}
{%- set ns = namespace(outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr((entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if not states('sensor.esphome_version') in device_version -%}
{%- set ns.outdated = ns.outdated + [ device_version ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- ns.outdated | count | int -}}
icon_template: >-
{%- set entities = integration_entities('esphome') | map('device_id') | unique | list -%}
{%- set ns = namespace(up2date = [], outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr((entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if states('sensor.esphome_version') in device_version -%}
{%- set ns.up2date = ns.up2date + [ entity ] -%}
{%- else -%}
{%- set ns.outdated = ns.outdated + [ entity ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- if ns.outdated | count >= 1 -%}
mdi:package-up
{%- elif ns.up2date | count >= 1 -%}
mdi:package-check
{%- else -%}
mdi:help-box
{%- endif -%}
value_template: >-
{%- set entities = integration_entities('esphome') | map('device_id') | unique | list -%}
{%- set ns = namespace(up2date = [], outdated = []) -%}
{%- for entity in entities -%}
{%- set device_version = device_attr((entity), 'sw_version') | regex_findall_index('(\d+\.\d+\.\d+)') | join -%}
{%- if device_version is defined and states(entity) != "unavailable" -%}
{%- if states('sensor.esphome_version') in device_version -%}
{%- set ns.up2date = ns.up2date + [ entity ] -%}
{%- else -%}
{%- set ns.outdated = ns.outdated + [ entity ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- if ns.outdated | count == 0 and ns.up2date | count >= 1 -%}
No update available
{%- elif ns.outdated | count == 1 -%}
Update available for one device
{%- elif ns.outdated | count >= 2 -%}
{{- "Updates available for " + ns.outdated | count | string + " devices"-}}
{%- else -%}
No ESPHome-device found
{%- endif -%}
Thanks @junkman690 for your contribution. The code snippet is primarily usefull for users not using Home Assistant OS but just for instance the Docker installation.
For sure, you can use the integrated version information if available instead of another sensor
Hi @dominik4545, I am running separate docker versions and the firmware info is listed on the device, however it is not integrated into notifications/upgrades like with HAOS. The templated code above for the sw_version attribute works in the docker version (I haven’t test in HAOS as I don’t run it).