Update notifications! Core, HACS, Supervisor and Addons

Those are alerts, see here for more info: https://www.home-assistant.io/integrations/alert/

There’s no default file to put alerts in like with automations, scripts, scenes, etc. They can either go into your configuration.yaml under alert: like it shows in that help link or you can use one of the options in splitting up the configuration to move them to a separate file. Using a package like I linked in my top post is one such option for splitting up the configuration.

I’m not super familiar with telegram but as long as you can make a notifier with platform: telegram (which it looks like you can) it should work. You can either make a telegram notifier called me or adjust this section above in all the alerts to the name you choose:

notifiers:
  - 'me'

I don’t know what kinds of options telegram notifiers accept for data but you’ll also probably need some adjustment there for each alert as that is specific to each type of notifier.

Will deal with it in following days.

Thank you for the prompt reply

Useful code, - thanks for sharing!

Any chance you know how to code a “new version” sensor for the System HassOS X.YZ…?

Cheers.

Haha yea I thought about that one too. So there is a /supervisor/host/info API but this is what I get back from it:

{
  "result": "ok",
  "data": {
    "chassis": "",
    "cpe": "",
    "features": [
      "reboot",
      "shutdown",
      "services",
      "hostname"
    ],
    "hostname": "hassio",
    "operating_system": "Raspbian GNU/Linux 10 (buster)",
    "deployment": "",
    "kernel": "4.19.66-v7l+"
  }
}

The problem here of course is that it doesn’t include anything about the version so there’s no way to tell from this API call if I need an update. This doesn’t make it impossible it just means I think I’d have to have something running on the host basically telling HA when its host has an update.

This was enough of a roadblock that it deterred me for now but I will probably revisit it at some point. Something like that would clearly be dependent on the host OS though so it would need to be modified per host type that HA can be deployed on.

That being said, you might actually want to try the API call for your own system first since I notice you have more info under Host System on yours then I do and it actually does list some version info (HassOS 3.13). From the HA CLI when you execute ha host info what do you see? Does the response include info on current version and newest version for you? If so that’s all you need to make a sensor like the ones above.

[EDIT] Wow, ok, you have identified a flaw in my plan my friend. I realized after you said this that I didn’t actually know how to update raspbian since I haven’t actually had it that long. Appears the answer is run apt update and now that I did it appears I have 280 upgradeable packages LOL

However I think this does make it scriptable as long as you can ssh into the host from HA so I’m going to dig into this. I think I can figure this out

The correct command would be:
sudo apt update && sudo apt upgrade -y

Update reads the package list and updates the ressources. Upgrade actually does the real process of upgrading software. The -y confirms all changes upfront (no interaction).

But, you have to be careful, this has some pitfalls in general. What you wouldn’t do with this, is a dist-upgrade (upgrading to a higher version of the OS). In theory you could do that as well, but I wouldn’t recommend that. Then it would be:
sudo apt update && sudo apt upgrade -y && sudo dist-upgrade -y

Oh, and there are a few more package managers out there, so apt is on some systems the way to go, others would need apt-get or aptitude.

Thanks! Yea I agree, auto-updating the host in the background seems a bit dangerous, I wouldn’t want to put that on a command_line sensor. I choose to put the actual update command onto a shell_command that I run when I know there are updates. Although just now when I did it it took down home assistant, I assume because a package it was using was updated. So yea, I definitely wouldn’t want to run that command automatically.

That being said @Tomahawk I did figure it out :slight_smile: I’ll share it below but I’m not going to add this one to the package because of a few important caveats:

  1. This is completely OS dependent. It looks specifically at the output of apt to see if there’s updates so if apt is the package manager your host uses it will need to be adjusted
  2. apt pops out this lovely warning message when you use it in this way WARNING: apt does not have a stable CLI interface. Use with caution in scripts. So yea, it appears they don’t really encourage this kind of usage and it may break in the future and require fixing. Just FYI
  3. It relies on being able to ssh into the host. To do this you’ll need to replace parts of the sensor with the username and address of your particular host, it won’t just work out of the box like the others above
  4. I set up my HA to be able to SSH into the host without a password. For anyone that doesn’t know how to do this, here’s a quick guide. Alternatively you can include your password but password-less SSH is the best practice for security.

With that out of the way, here it is. So like the others, first we need a sensor to tell us when there’s updates. And in this case since we’re using apt our sensor is going to return us a count of packages that need updates so I added a binary_sensor to go with it and then obviously an alert. Here’s those three:

sensor:
  # Sensor to track info on updates available for host
  # Warning: apt returns a message that its CLI is not stable enough for scripting. So be warned, this could break
  - platform: command_line
    name: Host updates
    scan_interval: 900
    command: 'ssh -o UserKnownHostsFile=/config/.ssh/known_hosts <INSERT HOST USERNAME>@<INSERT HOST ADDRESS> -i /config/.ssh/id_ed25519 ''sudo apt update > /dev/null && sudo apt list --upgradeable'' | jq --raw-input --slurp ''{ "packages": split("\n") | del(.[] | select(. == "" or . == "Listing...")) }'''
    value_template: '{{ value_json.packages | length }}'
    json_attributes:
    - packages

binary_sensor:
  - platform: template
    sensors:  
      # True if there's updates available for the host
      updater_host:
        friendly_name: 'Updater - Host'
        device_class: problem
        value_template: "{{ states('sensor.host_updates') | int > 0 }}"
        availability_template: "{{ (states('sensor.host_updates') | int(-1)) > -1 }}"

alert:
  # Host update is available - un-acknowledgeable, auto-dismiss, me only
  host_updates_available:
    name: Host has updates
    entity_id: binary_sensor.updater_host
    state: 'on'
    can_acknowledge: false
    repeat: 360
    title: "Update{% if states('sensor.host_updates') | int > 1 %}s{% endif %} for HA Host available"
    message: "Updates available for {{ states('sensor.host_updates') }} package{% if states('sensor.host_updates') | int > 1 %}s{% endif %} on host"
    notifiers:
    - 'me'
    data:
      tag: 'host_updates_available'
      ttl: 21600

NOTE: Run the ssh command in the host updates sensor once manually in the portainer add-on to get your known_hosts file updated, otherwise it won’t work. Or alternatively copy the necessary line into /config/.ssh/known_hosts file manually from some other machine so its set up. Otherwise the SSH command will fail behind the scenes.

So, what does this sensor do? It’s going to

  1. SSH into the host
  2. Run sudo apt update first to update the package list (ignoring any stdout of that command)
  3. If that went successfully, run sudo apt list --upgradeable to get the list of packages to update
  4. Use jq to create a JSON array out of that output and stick it in a field called packages ignoring rows that just say “Listing…” or are blank (since apt does that, remember, it doesn’t like being scripted)
  5. Set the value of the sensor to the length of that JSON array and store the list of packages into the packages attribute

Note that I set the scan_interval to 15 minutes since this one is doing a lot more work then the others.

But of course there is one difference with this one from the others, unlike the others we have no UI to send this to. There’s no place in Home Assistant that allows you to update the host. So let’s make one. With shell command we can make a service that tells our host to update all packages needing updates like so:

shell_command:
  # Updates packages on host
  update_host: 'ssh <INSERT HOST USERNAME>@<INSERT HOST ADDRESS> -i /config/.ssh/id_ed25519 ''sudo apt upgrade -y'''

Then you can include UI showing what needs updates with an update button up top. Here’s what I made for anyone interested. Note that it does depend on two HACS packages though, vertical stack in card and flex table. You’ll need to install them to use this as is. Or make a different UI, there’s lots of options

cards:
  - cards:
      - entity: sensor.host_updates
        name: Host packages with updates
        type: entity
      - icon: 'mdi:update'
        icon_height: 30px
        name: Update all
        tap_action:
          action: call-service
          service: notify.update_host
          service_data:
            message: Ignore
        type: button
    horizontal: true
    type: 'custom:vertical-stack-in-card'
  - columns:
      - attr_as_list: packages
        id: name
        modify: 'x.replace(/^(\S+)(\s.*)$/, ''$1'')'
        name: Name
      - attr_as_list: packages
        modify: 'x.replace(/^(\S+)(\s.*)$/, ''$2'')'
        name: Details
    entities:
      include: sensor.host_updates
    sort_by: name-
    strict: true
    type: 'custom:flex-table-card'
title: Host packages with updates
type: 'custom:vertical-stack-in-card'

And there we go! Now hopefully I’ll never build up that many host updates again haha.

3 Likes

can anyone tell me how to retrieve the current HassOS version thats running?
i am still running 3.12 , buw how can i get that in a sensor?

Take the first sensor out of this package:

sensor:
  # Sensor to track available updates for supervisor & addons
  - platform: command_line
    name: Supervisor updates
    command: 'curl http://supervisor/supervisor/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" | jq ''{"newest_version":.data.version_latest,"current_version":.data.version,"addons":[.data.addons[] | select(.version != .installed)]}'''
    value_template: "{{ value_json.addons | length }}"
    json_attributes:
    - newest_version
    - current_version
    - addons

But why not taking this package? That is exactly what you want, and someone else has already taken care of avoiding all the cliffs… :wink:

I’m not using HassOS so I can’t really try out the APIs on HassOS to see what’s available. But I think it might be available in the /supervisor/host/info API, I just can’t confirm that.

Do you have a way to use the home assistant CLI? If so could you run this command for me and let me know what the output is:
ha host info --raw-json

If anything in that response is something you don’t want to share feel free to replace those values with placeholders. I just need to see what the fields are that come back for you and if it includes info on HassOS current version and next version to know if a sensor is possible. Mine doesn’t when I run that command but I have a suspicion it behaves differently on HassOS since your developer tools page has more host info then mine.

aha, yes the HassOS version is present, here is output :slight_smile:


➜  ~ ha host info --raw-json
{"result": "ok", "data": {"chassis": "vm", "cpe": "cpe:2.3:o:home_assistant:hassos:3.12:*:production:*:*:*:ova:*", "features": ["reboot", "shutdown", "services", "hostname", "hassos"], "hostname": "hassio", "operating_system": "HassOS 3.12", "deployment": "production", "kernel": ➜  ~

is it possibe for me to retrieve it with the same curl command line?
i already have the current version, so thats fine

1 Like

Yep. If you change the command to curl http://supervisor/host/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" everything you need should be in that response.

When you copied from the command line it looks like it got cut short since it stops after "kernel":. But since it says HassOS 3.12 I think I can figure it out from that. If the only thing you want is the version number then I think something like this would work:

sensor:
  # Sensor to version of HassOS
  - platform: command_line
    name: HassOS Version
    command: 'curl http://supervisor/host/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" '
    value_template: "{{ value_json.data.operating_system[7:] }}"
    json_attributes:
    - data

I believe this will give you a sensor where the value is the current version of HassOS and the attributes contain the other details you have.

It doesn’t look like the next version is in there so I don’t think you can use this to alert on updates, just the current version you have installed. Unless the next version information is after kernel in the response since that’s as far as I can see.

1 Like

Thxn for that, gonna test tomorrow!! I already have a sensor for next version, so I can manage my notification :wink:

Oh nice! Care to share? I know @Tomahawk was looking for that info earlier and I didn’t know how to get it. Probably others would find that handy as well.

for current HassOS version, i use this one :

  - platform: rest
    resource: https://version.home-assistant.io/stable.json
    name: HassOS Version
    value_template: "{{ value_json.hassos.ova }}"
    scan_interval: 3600
1 Like

Hmm, this one gives me a “unknown” state
something wrong with it …

  # Sensor to version of HassOS
  - platform: command_line
    name: HassOS Version current
    command: 'curl http://supervisor/host/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" '
    value_template: "{{ value_json.data.operating_system[7:] }}"
    json_attributes:
    - data

edit: i see this in my log;

2020-04-21 09:49:11 ERROR (SyncWorker_3) [homeassistant.components.command_line.sensor] Command failed: curl http://supervisor/host/info -H Authorization: Bearer $(printenv SUPERVISOR_TOKEN)
2020-04-21 09:49:11 WARNING (SyncWorker_3) [homeassistant.components.command_line.sensor] Empty reply found when expecting JSON data

changed to code, below, that works :slight_smile:

  - platform: command_line
    name: HassOS Version current
    command: 'curl http://supervisor/host/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" | jq ''{"hassos":.data.operating_system}'''
    value_template: "{{ value_json.hassos[7:] }}"
3 Likes

Nice! Strange, I wouldn’t have expected jq would be required there since the output of the API call should already be JSON. Anyway glad you got it working :+1:

FYI for everyone that is making use of my “does my host have updates” alert, I noticed today that it was silently failing. The reason appears to be that on update of HA it overwrites the folder I had my SSH keys stored in (/root/.ssh by default)!

To counter this I re-ran ssh-keygen and generated the keys into /config/.ssh since the config folder is preserved from release to release. I then updated my sensor and shell command to include -i /config/.ssh/id_ed25519 to tell it where to look for the identity file since its not in the default location. This got it back working again so anyone using those should do a similar update. I edited the post to include these details already.

I don’t know if /config is the correct place for ssh key files from an architecture standpoint. But it seemed more appropriate then /ssl and /share which are the only other two folders I know are for sure preserved through updates. Plus there’s already .token files in /config for all integrations using oauth, not to mention all the auth information in .storage so it made sense to me.

1 Like

This is great, and I’m starting to implement all the sensors. But I’m trying to find a workaround for the addons, and here’s why. I’m not updating ESPHome to the latest because I want to stay on 1.13.6 which is stable for me. So my addon updater would always be on. But I do want to know if any other addons need to be updated. I thought if I could make a list of the addon names in a Lovelace card along with the other sensor information, I would at least have one place where I could see all the information about updates. Or maybe someone has a better idea. I’ve been messing around with templates for a couple of hours, but I’m new at this, and despite much reading of documentation and messing about in the template editor, I’m not getting anywhere. Anyone have any ideas? TIA!

If you want to specifically ignore one addon the way I would probably do it personally is by just modifying the jq to exclude that one.

Now I don’t use the ESPHome add-on and I don’t see it in my list so I’m just going to use the adguard add-on as an example. To tweak this for yours, just open up the ESPHome addon in HA and at the top in the URL bar it should look like this: ‘{HA Base URL}/hassio/addon/a0d7b954_adguard’. That last part in bold is the addon slug, replace the one for ESPHome where it says a0d7b954_adguard below.

Sensor will look like this instead:

# Sensor to track available updates for supervisor & addons
- platform: command_line
  name: Supervisor updates
  command: 'curl http://supervisor/supervisor/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" | jq ''{"newest_version":.data.version_latest,"current_version":.data.version,"addons":[.data.addons[] | select(.version != .installed and .slug != "a0d7b954_adguard")]}'''
  value_template: "{{ value_json.addons | length }}"
  json_attributes:
  - newest_version
  - current_version
  - addons

With this when building its list of addons that need updates it will exclude that one.

Alternatively, if you want to be able to present UI showing all addons that are out of date but simply not get notifications for the one you know is out if date you can modify the value_template instead of the command like so:

# Sensor to track available updates for supervisor & addons
- platform: command_line
  name: Supervisor updates
  command: 'curl http://supervisor/supervisor/info -H "Authorization: Bearer $(printenv SUPERVISOR_TOKEN)" | jq ''{"newest_version":.data.version_latest,"current_version":.data.version,"addons":[.data.addons[] | select(.version != .installed)]}'''
  value_template: "{{ value_json.addons | selectattr('slug', 'ne',  'a0d7b954_adguard') | list | length }}"
  json_attributes:
  - newest_version
  - current_version
  - addons

What this will do is it will still capture the full list of out of date addons and their current version in the addon attribute so you can show that list somewhere. But the state of the sensor will only increase if an addon other then the one you are excluding was updated. This way the alerts will only send you notifications when an addon you are tracking is updated.

1 Like

Thank you so much for your help, Mike - very much appreciated!!! The first sensor works as advertised, and I am happy to use that one. The other one does not work for me - I get the following error:

2020-04-28 16:53:04 ERROR (MainThread) [homeassistant.components.sensor] command_line: Error on device update!
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 324, in _async_add_entity
    await entity.async_device_update(warning=False)
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 476, in async_device_update
    await self.hass.async_add_executor_job(self.update)
  File "/usr/local/lib/python3.7/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/src/homeassistant/homeassistant/components/command_line/sensor.py", line 126, in update
    value, STATE_UNKNOWN
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 251, in render_with_possible_json_value
    error_value,
  File "/usr/local/lib/python3.7/concurrent/futures/_base.py", line 435, in result
    return self.__get_result()
  File "/usr/local/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 49, in run_callback
    future.set_result(callback(*args))
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 276, in async_render_with_possible_json_value
    return self._compiled.render(variables).strip()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "/usr/local/lib/python3.7/site-packages/jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
TypeError: object of type 'generator' has no len()