System Monitoring - How does yours look?

Sure, but please tell me about the specific part o anything. It’s too much code the entire view.

Would be really interested in seeing how you coded the left hand column, and also one of the bar charts e.g. for HDD

Thanks!

Screenshot 2023-02-22 at 11.43.25

1 Like

You can see how I did it here.

@RomanFdez - Sorry forgot to tag you!

Thanks for the reply, I’m very interested. I spent hours this week trying every command under the sun I could find to try and achieve this… Help! :smile:

Here is the guidance.

1.First create file on HA host machine where HA is installed:
/usr/local/bin/updatePackagesStatus.pl

With this code:

use File::Which;
use strict;

if(which('aptitude')) {
  open ( FILE, 'aptitude -F%p --disable-columns search ~U |') or die "$!\n";
} elsif (which('checkupdates')) {
  open ( FILE, 'checkupdates |' ) or die "$!\n";
} else {
  die "Error: neither checkupdates nor aptitude seem to be available\n";
}

my $pkgnbr = 0;
my $pkglist = "";
while (<FILE>){
  chomp;
  $pkglist = "$pkglist $_";+
  $pkgnbr++;
}
close (FILE);
open ( FILE, '> /home/amilino/updatestatus.log' ) or die "$!\n";
  print FILE "$pkglist   $pkgnbr upgradable(s)\n";
close (FILE);

This script is checking for updates and storing in /home/amilino/updatestatus.log information that you will later on use in HA. Of course you can change location and name of your log file.

2.Then create file (as well on host machine).
/etc/apt/apt.conf.d/99monitor

With this code:

# Update packages status
APT::Update::Post-Invoke {
    "echo 'Update Packages Status' && /usr/local/bin/updatePackagesStatus.pl";
};

Basically it will execute our script every time sudo apt-get update is executed. You can as well test it by running sudo apt-get update and see if you have any text created in /home/amilino/updatestatus.log file.

3.I also added in /etc/crontab this:

*/10 ** * * root apt-get update &

So that every 10 minutes apt-get update is executed automatically.

4.Now that your are done with HA host machine, you need to install PythonScriptsPro in HA.

5.You will also need generic script that can execute command and return output from any machine on the network via SSH.
I stored script here (be careful, this is inside HA docker, best to use File editor addon for adding this):
/config/scripts/remote_ssh_command.py

With this code:

from paramiko import SSHClient, AutoAddPolicy

host = self.config.get("host")
port = self.config.get("port")
username = self.config.get("user")
password = self.config.get("pass")
command = self.config.get("cmd")

client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(host, port, username, password)
stdin, stdout, stderr = client.exec_command(command)
resp = stdout.read()
stderr.read()
client.close()

self.state = f"\n{ resp.decode()[:250] }"
self._attr_extra_state_attributes = { "response": resp.decode() }

6.Add sensor.

- platform: python_script
  name: NUC Updates
  unique_id: "nuc_updates"
  icon: mdi:package-up
  scan_interval: 120
  file: scripts/remote_ssh_command.py
  host: !secret nuc_host
  port: !secret nuc_port
  user: "amilino"
  pass: !secret nuc_amilino_password
  cmd: >
    cat /home/amilino/updatestatus.log

7.If you want to place sensitive data into secrets file, you can have this in your /config/secrets.yaml:

nuc_host: 192.168.188.203
nuc_port: 20541
nuc_amilino_password: your_password

If not, then just replace !secret nuc_amilino_password with real password in previous step (same for other properties !secret nuc_host and !secret nuc_port).

8.And finally add card.

type: vertical-stack
cards:
  - type: entities
    entities:
      - entity: sensor.nuc_updates
        type: custom:template-entity-row
        name: Package(s)
        state: '{{ state_attr("sensor.nuc_updates", "response").split("   ")[1] }}'
        secondary: null
  - type: markdown
    content: |
      {{ state_attr("sensor.nuc_updates", "response").split("   ")[0] }}
    title: null
    style: |
      ha-card {
        border: 0px;
        {% if '0 upgradable(s)' in states('sensor.nuc_updates') -%}
          display: none
        {% endif %}
      }

I think that is all, hope I haven’t missed anything. Good thing is that you can use this setup to get any information from any server having SSH (OpenSSH on Windows is also working, so you can get system information about any windows machine), but you need to know how to construct command that is returning you wanted data, in this example cat command is used just to return content of log file. If you need some basic examples for linux and windows I can write it here as well, you can see from my example what I am returning and displaying in my cards.

This is for example if you want to get Distribution of your host machine:

- platform: python_script
  name: NUC Distribution
  unique_id: "nuc_distribution"
  icon: mdi:monitor
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret nuc_host
  port: !secret nuc_port
  user: "amilino"
  pass: !secret nuc_amilino_password
  cmd: >
    grep -Po "(?<=^PRETTY_NAME=).+" /etc/os-release | sed 's/"//g'

For this sensor I added very big interval, because this information is static and you don’t need to update your sensor every few seconds.

Let me know if you have any questions. :slight_smile:

For the bar-char look for bar-card on HACS

type: custom:bar-card
name: HDD
positions:
  icon: outside
  name: inside
color: '#17A589'
entities:
  - entity: sensor.disk_use_percent_home

And this is the code for the router cards.

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - show_name: true
        show_icon: false
        type: custom:button-card
        tap_action:
          action: none
        name: ANTARES
        color_type: card
        color: rgb(50,78,78)
        hold_action:
          action: none
        styles:
          card:
            - height: 10px
  - type: entities
    entities:
      - type: custom:button-card
        entity: binary_sensor.velop_mesh_wan_status
        show_name: false
        icon: hass:web
        size: 75px
        tap_action:
          action: none
        custom_fields:
          attr_dns_servers: '[[[ return entity.attributes.dns ]]]'
          attr_public_ip: '[[[ return entity.attributes.ip ]]]'
          attr_speedtest_latest: |
            [[[
              var entity_speedtest = states['sensor.velop_mesh_speedtest_latest']          
              var d = new Date(entity_speedtest.state)
              return d.toLocaleString()
            ]]]
          attr_speedtest_details: |
            [[[
              var round2 = (num) => Math.round(num * 100) / 100
              var spacing_internal = 5
              var spacing_external = 30
              var icon_size = 22
              var entity_speedtest = states['sensor.velop_mesh_speedtest_latest']
              var latency = entity_speedtest.attributes.latency
              var download_bandwidth = round2(entity_speedtest.attributes.download_bandwidth / 1000)
              var upload_bandwidth = round2(entity_speedtest.attributes.upload_bandwidth / 1000)        

              return `<span style="margin-right: ${spacing_external}px;">
                        <ha-icon icon="hass:swap-horizontal" style="width: ${icon_size}px;"></ha-icon>
                        <span>${latency}ms</span>
                      </span>
                      <span style="margin-right: ${spacing_external}px;">
                        <ha-icon icon="hass:cloud-download-outline" style="width: ${icon_size}px;"></ha-icon>
                        <span>${download_bandwidth} Mbps</span>
                      </span>
                      <span>
                        <ha-icon icon="hass:cloud-upload-outline" style="width: ${icon_size}px;"></ha-icon>
                        <span>${upload_bandwidth} Mbps</span>
                      </span>
                      `
            ]]]
        state:
          - value: 'on'
            color: darkcyan
          - value: 'off'
            color: darkred
        styles:
          card:
            - box-shadow: none
            - padding: 16px 8px
          grid:
            - grid-template-areas: >-
                "attr_dns_servers . attr_public_ip" "i i i"
                "attr_speedtest_details attr_speedtest_details
                attr_speedtest_details" "attr_speedtest_latest
                attr_speedtest_latest attr_speedtest_latest"
            - grid-template-rows: 5% 1fr 15% 5%
            - grid-template-columns: 1fr min-content 1fr
          custom_fields:
            attr_dns_servers:
              - justify-self: self-start
            attr_public_ip:
              - justify-self: self-end
        extra_styles: >
          div[id^="attr_"] { font-size: smaller; color:
          var(--disabled-text-color);

          }

          div[id^="attr_speedtest_"] { margin-top: 10px; }

          #attr_speedtest_latest::before { content: 'As at:' }

          #attr_public_ip::before { content: 'Public IP: ' }

          #attr_dns_servers::before { content: 'DNS: ' }
      - type: conditional
        conditions:
          - entity: binary_sensor.velop_mesh_speedtest_status
            state: 'on'
        row:
          type: section
      - type: custom:template-entity-row
        condition: '{{ is_state(''binary_sensor.velop_mesh_speedtest_status'', ''on'') }}'
        name: >-
          {{ state_attr('binary_sensor.velop_mesh_speedtest_status', 'status')
          }}
        tap_action:
          action: none
        card_mod:
          style: >
            state-badge { display: none; } state-badge + div.info { margin-left:
            8px !important; margin-right: 8px; text-align: center; }
      - type: section
      - type: custom:auto-entities
        card:
          type: horizontal-stack
        card_param: cards
        sort:
          method: friendly_name
        filter:
          include:
            - entity_id: /^(button|switch)\.velop_mesh_/
              options:
                type: custom:button-card
                hold_action:
                  action: more-info
                tap_action:
                  action: >-
                    [[[ return (entity.entity_id.startsWith("button")) ?
                    "call-service" : "toggle" ]]]
                  service: >-
                    [[[ return (entity.entity_id.startsWith("button")) ?
                    "button.press" : undefined ]]]
                  service_data:
                    entity_id: entity
                name: |-
                  [[[
                    var friendly_name = entity.attributes.friendly_name.replace("Velop Mesh:", "").trim()
                    var idx = friendly_name.lastIndexOf(" ");
                    var ret = friendly_name.substring(0, idx) + "<br />" + friendly_name.substring(idx + 1)
                    return ret
                  ]]]
                styles:
                  card:
                    - box-shadow: none
                    - margin-bottom: 3px
                  icon:
                    - animation: |-
                        [[[
                          var ret
                          if (entity.entity_id == "button.velop_mesh_start_speedtest" && states["binary_sensor.velop_mesh_speedtest_status"].state == "on") {
                            ret = "rotating 2s linear infinite"
                          }
                          return ret
                        ]]]
                    - color: |-
                        [[[ 
                          var ret
                          var col_on = "darkcyan"
                          var col_off = "var(--primary-text-color)"
                          ret = (entity.state == "on") ? col_on : col_off
                          if (entity.entity_id == "button.velop_mesh_start_speedtest") {
                            ret = (states["binary_sensor.velop_mesh_speedtest_status"].state == "on") ? col_on : col_off
                          }
                          return ret
                        ]]]
                  name:
                    - font-size: smaller
                    - color: |-
                        [[[
                          var ret
                          var col_on = "darkcyan"
                          var col_off = "var(--primary-text-color)"
                          ret = (entity.state == "on") ? col_on : col_off
                          if (entity.entity_id == "button.velop_mesh_start_speedtest") {
                            ret = (states["binary_sensor.velop_mesh_speedtest_status"].state == "on") ? col_on : col_off
                          }
                          return ret
                        ]]]
      - type: section
      - type: custom:fold-entity-row
        padding: 0
        head:
          type: custom:template-entity-row
          entity: sensor.velop_mesh_online_devices
          tap_action:
            action: fire-dom-event
            fold_row: true
          name: >-
            {% set friendly_name = state_attr(config.entity, 'friendly_name') %}
            {% if friendly_name %}
              {{ friendly_name.split(':')[1].strip() }}
            {% endif %}
          card_mod:
            style: |
              .info.pointer { font-weight: 500; }
              .state { margin-right: 10px; }
        entities:
          - type: custom:hui-element
            card_type: markdown
            card_mod:
              style:
                .: |
                  ha-card { border-radius: 0px; box-shadow: none; }
                  ha-markdown { padding: 16px 0px 0px !important; }
                ha-markdown$: >
                  table { width: 100%; border-collapse: separate;
                  border-spacing: 0px; }

                  tbody tr:nth-child(2n+1) { background-color:
                  var(--table-row-background-color); }

                  thead tr th, tbody tr td { padding: 4px 10px; }
            content: >
              {% set devices = state_attr('sensor.velop_mesh_online_devices',
              'devices') %} | # | Name | IP | Type |

              |:---:|:---|---:|:---:| {%- for device in devices -%}
                {% set idx = loop.index %}
                {%- for device_name, device_details in device.items() -%}
                  {%- set device_ip = device_details.ip -%}
                  {%- set connection_type = device_details.connection | lower -%}
                  {%- set guest_network = device_details.guest_network -%}
                  {%- if connection_type == "wired" -%}
                    {%- set connection_icon = "ethernet" -%}
                  {% elif connection_type == "wireless" -%}
                    {%- set connection_icon = "wifi" -%}
                  {% elif connection_type == "unknown" -%}
                    {%- set connection_icon = "help" -%}
                  {% else -%}
                    {%- set connection_icon = "" -%}
                  {%- endif %}
              {{ "| {} | {}{} | {} | {} |".format(idx, device_name,
              '&nbsp;<ha-icon icon="hass:account-multiple"></ha-icon>' if
              guest_network else '', device_ip, '<ha-icon icon="hass:' ~
              connection_icon ~ '"></ha-icon>') }}
                {%- endfor %}
              {%- endfor %}
      - type: custom:fold-entity-row
        padding: 0
        head:
          type: custom:template-entity-row
          entity: sensor.velop_mesh_offline_devices
          tap_action:
            action: fire-dom-event
            fold_row: true
          name: >-
            {% set friendly_name = state_attr(config.entity, 'friendly_name') %}
            {% if friendly_name %}
              {{ friendly_name.split(':')[1].strip() }}
            {% endif %}
          card_mod:
            style: |
              .info.pointer { font-weight: 500; }
              .state { margin-right: 10px; }
        entities:
          - type: custom:hui-element
            card_type: markdown
            card_mod:
              style:
                .: |
                  ha-card { border-radius: 0px; box-shadow: none; }
                  ha-markdown { padding: 16px 0px 0px !important; }
                ha-markdown$: >
                  table { width: 100%; border-collapse: separate;
                  border-spacing: 0px; }

                  tbody tr:nth-child(2n+1) { background-color:
                  var(--table-row-background-color); }

                  thead tr th, tbody tr td { padding: 4px 10px; }
            content: >
              {% set devices = state_attr('sensor.velop_mesh_offline_devices',
              'devices') %}

              | # | Name |

              |:---:|:---|

              {% for device in devices %} {{ "| {} | {} |".format(loop.index,
              device) }}

              {% endfor %}
      - type: conditional
        conditions:
          - entity: sensor.velop_mesh_available_storage
            state_not: '0'
          - entity: sensor.velop_mesh_available_storage
            state_not: unavailable
        row:
          type: custom:fold-entity-row
          padding: 0
          head:
            type: section
            label: Storage
          entities:
            - type: custom:hui-element
              card_type: markdown
              card_mod:
                style:
                  .: |
                    ha-card { border-radius: 0px; box-shadow: none; }
                    ha-markdown { padding: 16px 0px 0px !important; }
                  ha-markdown$: >
                    table { width: 100%; border-collapse: separate;
                    border-spacing: 0px; }        

                    tbody tr:nth-child(2n+1) { background-color:
                    var(--table-row-background-color); }

                    thead tr th, tbody tr td { padding: 4px 10px; }
              content: >
                {% set partitions =
                state_attr('sensor.velop_mesh_available_storage', 'partitions')
                %}

                | Host | Label | %age used

                |:---:|:---:|:---:|

                {% for partition in partitions %} {{ "| {} | {} | {}
                |".format(partition.ip, partition.label, partition.used_percent)
                }}

                {% endfor %}
    state_color: false
    card_mod:
      style:
        .: |
          #states { padding-top: 0px; }
        fold-entity-row:
          $:
            template-entity-row:
              $: |
                state-badge { display: none; }
                state-badge + div { margin-left: 8px !important; }
                .info.pointer { font-weight: 500; }
                .state { margin-right: 10px; }
  - type: custom:auto-entities
    card:
      type: vertical-stack
    card_param: cards
    sort:
      method: friendly_name
      reverse: false
    filter:
      include:
        - entity_id: /^binary_sensor\.velop_(?!(mesh)).*_status/
          options:
            type: custom:config-template-card
            variables:
              BUTTONS: |
                () => {
                  var ret = []
                  var entity_prefix = "button." + "this.entity_id".split(".")[1].split("_").slice(0, -1).join("_")
                  for (var entity_id in states) {
                    if (entity_id.startsWith(entity_prefix)) {
                      var entity_action = states[entity_id].attributes.friendly_name.split(':')[1].trim()
                      var entity_name = states[entity_id].attributes.friendly_name.split(':')[0].replace('Velop', '').trim()
                      ret.push({
                        'entity': entity_id,
                        'name': entity_action,
                        'tap_action': {
                            'action': 'call-service',
                            'service': 'linksys_velop.' + entity_action.toLowerCase() + '_node',
                            'service_data': {
                              'node_name': entity_name,
                            },
                            'confirmation': {
                              'text': 'Are you sure you want to reboot the ' + entity_name + ' node?'
                          }
                        }
                      })
                    }
                  }
                  return ret
                }
              ID_CONNECTED_DEVICES: >
                "sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
                -1).join("_") + "_connected_devices"
              ID_ENTITY_PICTURE: >
                "sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
                -1).join("_") + "_image"
              ID_LAST_UPDATE_CHECK: >
                "sensor." + "this.entity_id".split(".")[1].split("_").slice(0,
                -1).join("_") + "_last_update_check"
              ID_MODEL: >
                "sensor." +
                "this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
                + "_model"
              ID_PARENT: >
                "sensor." +
                "this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
                + "_parent"
              ID_SERIAL: >
                "sensor." +
                "this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
                + "_serial"
              ID_UPDATE_AVAILABLE: >
                "update." +
                "this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
                + "_update"
              CONNECTED_DEVICES_TEXT: |
                (entity_id) => {
                  var ret = `
                | # | Name | IP | Type |
                |:---:|:---|---:|:---:|
                `
                  if (states[entity_id].attributes.devices) {
                    states[entity_id].attributes.devices.forEach((device, idx) => {
                      var connection_icon
                      switch (device.type.toLowerCase()) {
                        case "wireless":
                          connection_icon = "wifi"
                          break
                        case "wired":
                          connection_icon = "ethernet"
                          break
                        case "unknown":
                          connection_icon = "help"
                          break
                      }
                      ret += "| " + (idx + 1) + " | " + device.name + ((device.guest_network) ? "&nbsp;<ha-icon icon='hass:account-multiple'></ha-icon>" : "") + " | " + device.ip + " | <ha-icon icon='hass:" + connection_icon + "'></ha-icon> |\n"
                    })
                  }
                  return ret
                }
              getEntityPicture: |
                () => {
                  if (states[vars['ID_ENTITY_PICTURE']]) {
                    return states[vars['ID_ENTITY_PICTURE']].state
                  } else {
                    return '/local/velop_nodes/' + states[vars['ID_MODEL']].state + '.png'
                  }
                }
            entities:
              - this.entity_id
              - ${ID_CONNECTED_DEVICES}
              - ${ID_LAST_UPDATE_CHECK}
              - ${ID_MODEL}
              - ${ID_PARENT}
              - ${ID_SERIAL}
              - ${ID_UPDATE_AVAILABLE}
            card:
              type: entities
              card_mod:
                style:
                  .: |
                    #states { padding-top: 0px; }
                  fold-entity-row:
                    $:
                      template-entity-row:
                        $: |
                          state-badge { display: none; }
                          state-badge + div { margin-left: 8px !important; }
                          .info.pointer { font-weight: 500; }
                          .state { margin-right: 10px; }
              entities:
                - type: custom:button-card
                  entity: this.entity_id
                  size: 100%
                  show_entity_picture: true
                  show_last_changed: true
                  show_state: true
                  tap_action:
                    action: none
                  entity_picture: ${ getEntityPicture() }
                  name: |
                    [[[
                      var ret = entity.attributes.friendly_name
                      if (ret) {
                        ret = ret.replace("Velop", "").split(":")[0].trim()
                      }
                      return ret || "N/A"
                    ]]]
                  state_display: |
                    [[[
                      return `<ha-icon 
                        icon="hass:checkbox-blank-circle"
                        style="width: 24px; height: 24px;">
                        </ha-icon>`
                    ]]]
                  custom_fields:
                    attr_label_model: Model
                    attr_model: ${states[ID_MODEL].state}
                    attr_label_serial: Serial
                    attr_serial: ${states[ID_SERIAL].state}
                    attr_parent: >-
                      ${(states[ID_PARENT].state && states[ID_PARENT].state !=
                      'unknown') ? 'Connected to ' + states[ID_PARENT].state :
                      'N/A'}
                    attr_label_ip: IP Address
                    attr_ip: '[[[ return entity.attributes.ip || ''N/A'' ]]]'
                    attr_update: |
                      [[[
                        var ret
                        var entity_update = 'update.' + entity.entity_id.split('.')[1].split('_').slice(0, -1).join('_') + '_update'
                        var update_available = states[entity_update].state
                        if (update_available == 'on') {
                          ret = `<ha-icon
                              icon="hass:package-up"
                              style="width: 24px; height: 20px;"
                            >
                            </ha-icon>`
                        }
                        return ret
                      ]]]
                  extra_styles: >
                    div[id^="attr_"] { justify-self: start; }
                    div[id^="attr_label_"] { justify-self: start; margin-left:
                    20px; } #label, #attr_parent { padding-top: 25px; font-size:
                    smaller; }
                  styles:
                    card:
                      - box-shadow: none
                      - padding: 10px 8px
                    grid:
                      - grid-template-areas: >-
                          "n n attr_update s" "i attr_label_model
                          attr_label_model attr_model" "i attr_label_serial
                          attr_label_serial attr_serial" "i attr_label_ip
                          attr_label_ip attr_ip" "l l l attr_parent "
                      - grid-template-columns: 30% 1fr 30px 150px
                    name:
                      - font-size: large
                      - justify-self: start
                      - padding-bottom: 20px
                    label:
                      - justify-self: start
                    custom_fields:
                      attr_label_last_update_check:
                        - margin-left: 0px
                      attr_last_update_check:
                        - justify-self: end
                      attr_parent:
                        - justify-self: end
                      attr_update:
                        - color: darkred
                        - justify-self: end
                        - padding-bottom: 25px
                      attr_label_model:
                        - color: white
                        - font-size: 16px
                        - justify-self: start
                      attr_label_serial:
                        - color: white
                        - font-size: 16px
                        - justify-self: start
                      attr_label_ip:
                        - color: white
                        - font-size: 16px
                        - justify-self: start
                      attr_model:
                        - color: darkwhite
                        - font-size: 12px
                        - justify-self: end
                      attr_serial:
                        - color: darkwhite
                        - font-size: 12px
                        - justify-self: end
                      attr_ip:
                        - color: darkwhite
                        - font-size: 12px
                        - justify-self: end
                    state:
                      - justify-self: end
                      - padding-bottom: 20px
                      - color: |-
                          [[[
                            return (entity.state == 'on' ? 'darkcyan' : 'darkred')
                          ]]]
                - type: custom:auto-entities
                  show_empty: true
                  card:
                    type: custom:fold-entity-row
                    head:
                      type: section
                      label: Additional Information
                    padding: 0
                  filter:
                    include:
                      - entity_id: ${ID_LAST_UPDATE_CHECK}
                        options:
                          name: Last update check
                      - entity_id: ${ID_PARENT}
                        not:
                          state: unknown
                        options:
                          type: custom:template-entity-row
                          name: Backhaul
                          state: >-
                            {% set backhaul_info = state_attr(config.entity,
                            'backhaul') %} {% set backhaul_speed =
                            backhaul_info.speed_mbps | round(2) %} {% if
                            (backhaul_speed | string).split('.')[1] == '0' %}
                              {% set backhaul_speed = backhaul_speed | int %}
                            {% endif %} {{ backhaul_info.connection }} ({{
                            backhaul_speed }} Mbps)
                - type: custom:auto-entities
                  show_empty: false
                  filter:
                    include:
                      - domain: button
                        entity_id: >-
                          ${"/" +
                          "this.entity_id".split(".")[1].split("_").slice(0,-1).join("_")
                          + "/"}
                  card:
                    type: custom:fold-entity-row
                    padding: 0
                    head:
                      type: section
                      label: Actions
                    entities:
                      - type: buttons
                        entities: ${BUTTONS()}
                - type: section
                - type: custom:fold-entity-row
                  padding: 0
                  head:
                    type: custom:template-entity-row
                    tap_action:
                      action: fire-dom-event
                      fold_row: true
                    entity: ${ID_CONNECTED_DEVICES}
                    name: >-
                      {% set name = state_attr(config.entity, 'friendly_name')
                      %} {% if name %}
                        {{ name.split(':')[1].strip() }}
                      {% endif %}
                  entities:
                    - type: custom:hui-element
                      card_type: markdown
                      content: ${CONNECTED_DEVICES_TEXT(ID_CONNECTED_DEVICES)}
                      card_mod:
                        style:
                          .: |
                            ha-card { border-radius: 0px; box-shadow: none; }
                            ha-markdown { padding: 16px 0px 0px !important; }
                          ha-markdown$: >
                            table { width: 100%; border-collapse: collapse; }

                            tbody tr:nth-child(2n+1) { background-color:
                            var(--table-row-background-color); }

                            thead tr th, tbody tr td { padding: 4px 10px; }

1 Like

Thanks @RomanFdez, thats really helpful (great work too!)

1 Like

This setup for showing multiple temperature looks great. I was not able to find it on the repository. Is it available? Or even a sample code. I am using the card already for my server vitals and love it

Anyone knowing how to get disk throughput (read / write local disk) as a sensor? Like this which is from a virtual environment (outside HA):

Bild

Setup: Raspberry Pi with HA OS. Does the supervisor has this information?

Thanks all for the inspiration, now I just need to create a dashboard for everything else

type: custom:stack-in-card
keep:
  border_radius: true
  margin: true
cards:
  - type: custom:button-card
    name: Network Server
    entity_picture: /local/images/raspberry-pi.png
    show_name: true
    show_entity_picture: true
    styles:
      card:
        - padding: 5px
        - border: 0px
      grid:
        - grid-template-areas: '"n i"'
        - grid-template-columns: 3fr 1fr
      name:
        - justify-self: start
        - padding-left: 20px
        - font-size: 30px
        - font-weight: 300
  - type: horizontal-stack
    cards:
      - type: custom:mini-graph-card
        entities:
          - sensor.networkserver_cpu_usage
        hours_to_show: 24
        points_per_hour: 2
        hour24: true
        animate: true
        height: 150
        show:
          extrema: true
          icon: false
          name: false
        color_thresholds:
          - value: 100
            color: '#d32f2f'
          - value: 90
            color: '#ffa000'
          - value: 75
            color: '#388e3c'
        card_mod:
          style: |
            ha-card .states.flex{
              padding-bottom: 0px;
              }
            ha-card .graph{
              margin-top: 0px !important;
            }
            ha-card .graph .graph__legend{
              padding-bottom: 0px !important;
            }
            ha-card .info.flex{
              padding-bottom: 0px !important;
            }
      - type: custom:mini-graph-card
        entities:
          - sensor.networkserver_temperature
        hours_to_show: 24
        points_per_hour: 2
        hour24: true
        animate: true
        height: 150
        show:
          extrema: true
          icon: false
          name: false
        color_thresholds:
          - value: 100
            color: '#d32f2f'
          - value: 85
            color: '#ffa000'
          - value: 60
            color: '#388e3c'
        card_mod:
          style: |
            ha-card .states.flex{
              padding-bottom: 0px;
              }
            ha-card .graph{
              margin-top: 0px !important;
            }
            ha-card .graph .graph__legend{
              padding-bottom: 0px !important;
            }
            ha-card .info.flex{
              padding-bottom: 0px !important;
            }
  - type: horizontal-stack
    cards:
      - type: custom:bar-card
        entities:
          - sensor.networkserver_cpu_usage
        name: CPU Usage
        positions:
          icon: 'off'
          name: inside
          value: inside
          indicator: inside
        severity:
          - color: '#d32f2f'
            from: 90
            to: 100
          - color: '#ffa000'
            from: 75
            to: 90
          - color: '#388e3c'
            from: 0
            to: 75
        card_mod:
          style: |
            bar-card-backgroundbar {
              border-radius: 8px;
            }
            bar-card-currentbar {
              border-radius: 8px;
            }
      - type: custom:bar-card
        entities:
          - sensor.networkserver_temperature
        name: CPU Temp
        positions:
          icon: 'off'
          name: inside
          value: inside
          indicator: inside
        severity:
          - color: '#d32f2f'
            from: 85
            to: 100
          - color: '#ffa000'
            from: 60
            to: 85
          - color: '#388e3c'
            from: 0
            to: 60
        card_mod:
          style: |
            bar-card-backgroundbar {
              border-radius: 8px;
            }
            bar-card-currentbar {
              border-radius: 8px;
            }
  - type: horizontal-stack
    cards:
      - type: custom:bar-card
        entities:
          - entity: sensor.networkserver_load_1m
            name: Load 1m
          - entity: sensor.networkserver_load_5m
            name: Load 5m
          - entity: sensor.networkserver_load_15m
            name: Load 15m
        stack: horizontal
        max: 4
        severity:
          - color: '#d32f2f'
            from: 2
            to: 4
          - color: '#ffa000'
            from: 1
            to: 2
          - color: '#388e3c'
            from: 0
            to: 1
        positions:
          icon: 'off'
          name: inside
          value: inside
          indicator: inside
        card_mod:
          style: |
            bar-card-card{
              margin-right: 20px
            }
            bar-card-backgroundbar {
              border-radius: 8px;
            }
            bar-card-currentbar {
              border-radius: 8px;
            }
  - type: custom:bar-card
    entities:
      - sensor.networkserver_memory_use
    name: Memory Usage
    width: 70%
    positions:
      icon: 'off'
      name: inside
      value: inside
      indicator: inside
    severity:
      - color: '#d32f2f'
        from: 90
        to: 100
      - color: '#ffa000'
        from: 75
        to: 90
      - color: '#388e3c'
        from: 0
        to: 75
    card_mod:
      style: |
        bar-card-backgroundbar {
          border-radius: 8px;
        }
        bar-card-currentbar {
          border-radius: 8px;
        }
  - type: custom:bar-card
    entities:
      - sensor.networkserver_disk_use
    name: Disk Usage
    width: 70%
    positions:
      icon: 'off'
      name: inside
      value: inside
      indicator: inside
    severity:
      - color: '#d32f2f'
        from: 90
        to: 100
      - color: '#ffa000'
        from: 75
        to: 90
      - color: '#388e3c'
        from: 0
        to: 75
    card_mod:
      style: |
        bar-card-backgroundbar {
          border-radius: 8px;
        }
        bar-card-currentbar {
          border-radius: 8px;
        }
  - type: custom:mini-graph-card
    entities:
      - entity: sensor.networkserver_network_download
        color: '#91C0F8'
        name: Download
      - entity: sensor.networkserver_network_upload
        color: '#ffa000'
        name: Upload
        show_state: true
    hours_to_show: 24
    points_per_hour: 2
    height: 75
    animate: true
    line_width: 2.5
    show:
      name: false
      icon: false
    card_mod:
      style: |
        ha-card .graph{
          margin-top: -15px !important;
        }
        ha-card .graph .graph__legend{
          padding-bottom: 0px !important;
        }
  - type: entities
    entities:
      - entity: sensor.networkserver_uptime
        name: Uptime
      - entity: sensor.networkserver_updates
        name: Packages
      - type: custom:fold-entity-row
        head:
          type: section
          label: Server Details
        card_mod:
          style:
            hui-sensor-entity-row:
              $ hui-generic-entity-row $: |
                .pointer{
                  color: #91C0F8;
                }
                .text-content{
                  color: #e1e1e1;
                }
            hui-simple-entity-row:
              $ hui-generic-entity-row $: |
                .pointer{
                  color: #91C0F8;
                }
                .text-content{
                  color: #e1e1e1;
                }
        entities:
          - entity: sensor.networkserver_hostname
            name: Hostname
          - entity: sensor.networkserver_host_ip
            name: IP Address
          - entity: sensor.networkserver_host_os
            name: OS
          - entity: sensor.networkserver_host_kernel
            name: Kernel
          - entity: sensor.networkserver_host_platform
            name: Platform
          - entity: sensor.networkserver_host_architecture
            name: Architecture
          - entity: binary_sensor.networkserver_under_voltage
            name: Power
          - entity: sensor.networkserver_last_message
            name: Last Refresh
    card_mod:
      style:
        hui-sensor-entity-row:
          $ hui-generic-entity-row $: |
            .pointer{
              color: #91C0F8;
            }
            .text-content{
              color: #e1e1e1;
            }
14 Likes

Great looking dashboard, kudos for sharing the code.

me too, would love to see the code plz

You can grab the above code copy+paste it all for the Network Server. Then copy paste for your other 3 servers and change the entities. That’ll make the total of 4.

type: custom:stack-in-card
keep:
  border_radius: true
  margin: true
cards:
  - type: custom:button-card
    name: **Network Server**
    entity_picture: /local/images/raspberry-pi.png
    show_name: true
    show_entity_picture: true
    styles:
etc
etc
etc

Hope to be of some help. Good luck!

2 Likes

I know this old, but can you share how you use the pythonscripts for this?
want to do it with windows and linux systems.
thanks for your help

Sure for the setup you can check this post.

To get the actual sensors, I isolated things in separate configuration file stored in /config/sensors_sys_monitoring.yaml

# 1NUC (Linux machine)
- platform: systemmonitor
  resources:
    - type: disk_use_percent
      arg: /
    - type: disk_use
      arg: /
    - type: disk_free
      arg: /
    - type: memory_use_percent
    - type: memory_use
    - type: memory_free
    - type: swap_use_percent
    - type: swap_use
    - type: swap_free
    - type: processor_use
    - type: processor_temperature
    - type: last_boot

- platform: python_script
  name: 1NUC Updates
  unique_id: "1nuc_updates"
  icon: mdi:package-up
  scan_interval: 120
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    cat /home/amilino/updatestatus.log

- platform: python_script
  name: 1NUC Model
  unique_id: "1nuc_model"
  icon: mdi:desktop-tower
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    sudo dmidecode -s system-product-name

- platform: python_script
  name: 1NUC Distribution
  unique_id: "1nuc_distribution"
  icon: mdi:monitor
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    grep -Po "(?<=^PRETTY_NAME=).+" /etc/os-release | sed 's/"//g'

- platform: python_script
  name: 1NUC Kernel
  unique_id: "1nuc_kernel"
  icon: mdi:developer-board
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    uname -msr

- platform: python_script
  name: 1NUC Processor
  unique_id: "1nuc_processor"
  icon: mdi:chip
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    grep -m 1 "model name" /proc/cpuinfo | sed -e "s/^.*: //"

- platform: python_script
  name: 1NUC Processor speed
  unique_id: "1nuc_processor_speed"
  unit_of_measurement: MHz
  icon: mdi:rotate-360
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    lscpu | grep "CPU MHz" | sed -e "s/^.*: //" | awk '{printf("%.f \n",$1)}'

- platform: python_script
  name: 1NUC Processor voltage
  unique_id: "1nuc_processor_voltage"
  unit_of_measurement: V
  icon: mdi:lightning-bolt-outline
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    sudo dmidecode --type processor | grep "Voltage" | grep -Eo "[0-9]+\.[0-9]+"

- platform: python_script
  name: 1NUC Processor mem x86_64
  unique_id: "1nuc_processor_mem_x86_64"
  unit_of_measurement: MiB
  icon: mdi:memory
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    helper_functions -kb2mborgb $(grep "Slab" /proc/meminfo | grep -Eo "[0-9]+") | sed 's/M//' | awk '{printf("%.f \n",$1)}'

- platform: python_script
  name: 1NUC Processor mem gpu
  unique_id: "1nuc_processor_mem_gpu"
  unit_of_measurement: MiB
  icon: mdi:expansion-card
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    lspci -v -s 00:02.0 | grep " prefetchable" | grep -Eo "[0-9]+[0-9]+[0-9]M" | sed 's/M//'

- platform: python_script
  name: 1NUC Processor scaling governor
  unique_id: "1nuc_processor_scaling_governor"
  icon: mdi:scale-balance
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

- platform: python_script
  name: 1NUC Top Processes cpu usage
  unique_id: "1nuc_processes_cpu_usage"
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    top -b -n 1 | grep -ve "jq" -ve "top" | awk 'FNR>=6 && FNR<=10{print $12, $9}' | jq -nR '{"return": [inputs | split(" ") | { "proc": .[0], "usage": .[1] }]}'

- platform: python_script
  name: 1NUC Top Processes memory usage
  unique_id: "1nuc_processes_memory_usage"
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    top -o %MEM -b -n 1 | grep -ve "jq" -ve "top" | awk 'FNR>=6 && FNR<=10{print $12, $10}' | jq -nR '{"return": [inputs | split(" ") | { "proc": .[0], "usage": .[1] }]}'

- platform: python_script
  name: 1NUC Processes
  unique_id: "1nuc_processes"
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: >
    sudo ps -N --format comm,etime,cmd | grep -v grep | grep -w "/usr/bin/kodi\|webgrabplus" | awk '{print $1, $2}' | jq -nR '{"return": [inputs | split(" ") | { "proc": .[0], "etime": .[1] }]}'

- platform: python_script
  name: 1NUC Services
  unique_id: "1nuc_services"
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 1nuc_host
  port: !secret 1nuc_port
  user: "amilino"
  pass: !secret 1nuc_amilino_password
  cmd: > 
    for i in "smbd" "ssh" "xrdp"; do printf "$i\t"; sudo service "$i" status | grep "Active: " | sed -e "s/^.*; //"; done | jq -nR '{"return": [inputs | split("\t") | { "service": .[0], "etime": .[1] }]}'

# 2NUC (Windows machine)
- platform: python_script
  name: 2NUC Processor
  unique_id: "2nuc_processor"
  icon: mdi:chip
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    wmic CPU get NAME | more +1

- platform: python_script
  name: 2NUC Model
  unique_id: "2nuc_model"
  icon: mdi:desktop-tower
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell (Get-WmiObject -Class:Win32_ComputerSystem).Model

- platform: python_script
  name: 2NUC Distribution
  unique_id: "2nuc_distribution"
  icon: mdi:monitor
  scan_interval: 63072000 #2 Years in seconds
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell (Get-WmiObject -class Win32_OperatingSystem).Caption

- platform: python_script
  name: 2NUC Processor use
  unique_id: "2nuc_processor_use"
  icon: mdi:cpu-64-bit
  unit_of_measurement: '%'
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\processor_use.ps1

- platform: python_script
  name: 2NUC Processor temperature
  unique_id: "2nuc_processor_temperature"
  icon: mdi:thermometer
  unit_of_measurement: 'ºC'
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\processor_temperature.ps1

- platform: python_script
  name: 2NUC Memory use percent
  unique_id: "2nuc_memory_use_percent"
  icon: mdi:memory
  unit_of_measurement: '%'
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\memory_use_percent.ps1

- platform: python_script
  name: 2NUC Memory use
  unique_id: "2nuc_memory_use"
  icon: mdi:memory
  unit_of_measurement: 'MiB'
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\memory_use.ps1

- platform: python_script
  name: 2NUC Memory free
  unique_id: "2nuc_memory_free"
  icon: mdi:memory
  unit_of_measurement: 'MiB'
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\memory_free.ps1

- platform: python_script
  name: 2NUC Last boot
  unique_id: "2nuc_last_boot"
  icon: mdi:clock
  scan_interval: 15
  file: scripts/remote_ssh_command.py
  host: !secret 2nuc_host
  port: !secret 2nuc_port
  user: "amilino"
  pass: !secret 2nuc_amilino_password
  cmd: >
    powershell c:\\SystemMonitoring\\last_boot.ps1

After you created file you need to add this into /config/configuration.yaml
sensor sys monitoring: !include sensors_sys_monitoring.yaml

For windows machine I created powershell files on windows machine itself (you also need OpenHardwareMonitor running in the background).
processor_use.ps1

# Check if Open Hardware Monitor is running.
if ((Get-Process -Name OpenHardwareMonitor -ErrorAction SilentlyContinue) -eq $null) {
    write-host 'OpenHardwareMonitor.exe not running!'
    exit 3
}

# Get the temperatures from all the found sensors and check them
$temperatures = Get-WmiObject -Namespace "Root\OpenHardwareMonitor" -Query "SELECT value FROM Sensor WHERE Name LIKE '%CPU Total%' AND Sensortype='Load'" | sort-object Identifier | select -First 1

$temperature_string = foreach ($result in $temperatures){
    '{0}' -f [math]::Round($result.Value)
}
 
write-host "$temperature_string"
exit 00

processor_temperature.ps1

# Check if Open Hardware Monitor is running.
if ((Get-Process -Name OpenHardwareMonitor -ErrorAction SilentlyContinue) -eq $null) {
    write-host 'OpenHardwareMonitor.exe not running!'
    exit 3
}

# Get the temperatures from all the found sensors and check them
$temperatures = Get-WmiObject -Namespace "Root\OpenHardwareMonitor" -Query "SELECT value FROM Sensor WHERE Name LIKE '%CPU Core%' AND Sensortype='Temperature'" | sort-object Identifier | select -First 1

$temperature_string = foreach ($result in $temperatures){
    '{0}' -f $result.Value
}
 
write-host "$temperature_string"
exit 0

memory_use_percent.ps1

# Check if Open Hardware Monitor is running.
if ((Get-Process -Name OpenHardwareMonitor -ErrorAction SilentlyContinue) -eq $null) {
    write-host 'OpenHardwareMonitor.exe not running!'
    exit 3
}

# Get the temperatures from all the found sensors and check them
$values= Get-WmiObject -Namespace "Root\OpenHardwareMonitor" -Query "SELECT value FROM Sensor WHERE Name = 'Memory' AND Sensortype='Load'" | sort-object Identifier | select -First 1

$value_string = foreach ($result in $values){
    '{0}' -f [math]::Round($result.Value,1) -Replace ',','.'
}
 
write-host "$value_string"
exit 0

memory_use.ps1

# Check if Open Hardware Monitor is running.
if ((Get-Process -Name OpenHardwareMonitor -ErrorAction SilentlyContinue) -eq $null) {
    write-host 'OpenHardwareMonitor.exe not running!'
    exit 3
}

# Get the temperatures from all the found sensors and check them
$values= Get-WmiObject -Namespace "Root\OpenHardwareMonitor" -Query "SELECT value FROM Sensor WHERE Name = 'Used Memory' AND Sensortype='Data'" | sort-object Identifier | select -First 1

$value_string = foreach ($result in $values){
    '{0}' -f [math]::Round($result.Value*1024,1) -Replace ',','.'
}
 
write-host "$value_string"
exit 0

memory_free.ps1

# Check if Open Hardware Monitor is running.
if ((Get-Process -Name OpenHardwareMonitor -ErrorAction SilentlyContinue) -eq $null) {
    write-host 'OpenHardwareMonitor.exe not running!'
    exit 3
}

# Get the temperatures from all the found sensors and check them
$values= Get-WmiObject -Namespace "Root\OpenHardwareMonitor" -Query "SELECT value FROM Sensor WHERE Name = 'Available Memory' AND Sensortype='Data'" | sort-object Identifier | select -First 1

$value_string = foreach ($result in $values){
    '{0}' -f [math]::Round($result.Value*1024,1) -Replace ',','.'
}
 
write-host "$value_string"
exit 0

last_boot.ps1

$os = Get-WmiObject -Class win32_operatingsystem 
$os.ConvertToDateTime($os.LastBootUpTime).ToString('yyyy-MM-ddTHH:mm:ss+00:00')

Let me know if you need as well code for the lovelace card.

1 Like

I have been using similar to your code for several years. They always worked well until 2023.4.x (not sure which release caused it to fail. Does yours still work?

I’m afraid I can’t say. I don’t use that anymore and I haven’t for a long, long time!

1 Like

If have been using the following code for a couple of years with no issues until core 2023.4x was released. Not sure which of the updates cause it to fail.

  - type: markdown
    title: Are Updates Available?
    card_mod:
      style: |
        :host {
          --card-mod-icon-color: #42a5f5;
          font-size: 1.1em;
        }
    content: |
      <ha-icon icon="mdi:home-assistant"></ha-icon>&nbsp;&nbsp;&nbsp;Add-ons needing update: &nbsp;&nbsp; {{ states('sensor.supervisor_updates') | default }}
      > {% for addon in state_attr('sensor.supervisor_updates', 'addons') %}
      >   &nbsp;&nbsp;{{ addon.name }}&nbsp; {{ addon.version }} -> {{ addon.version_latest }}
      > {% endfor %}
      
      <ha-icon icon="hacs:hacs"></ha-icon>&nbsp;&nbsp;&nbsp;HACS updates available: &nbsp;&nbsp; {{ states('sensor.hacs') | default }}
      > {% for repo in state_attr('sensor.hacs', 'repositories') %}
      >   &nbsp;&nbsp;{{ repo.display_name }} {{ repo["installed_version"] }} -> {{ repo["available_version"] }}
      > {% endfor %}
      
      |          |    Current | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Latest |
      |--- |:---: |:---: |:---: |
      | **Supervisor**&nbsp;&nbsp;&nbsp; | {{ state_attr('sensor.supervisor_updates', 'current_version') }} | | {{ state_attr('sensor.supervisor_updates', 'newest_version') }} |
      | **HassOS** | {{ states('sensor.home_assistant_operating_system_version') }} | | {{ states('sensor.home_assistant_operating_system_newest_version') }} |
      | **CORE** | {{ states.sensor.updater_core.attributes.current_version }} | | {{ states.sensor.updater_core.attributes.newest_version }} |
      | **Audio** | {{ states.sensor.updater_audio.attributes.current_version }} | | {{ states.sensor.updater_audio.attributes.newest_version }} |
      | **CLI** | {{ states.sensor.updater_cli.attributes.current_version }} | | {{ states.sensor.updater_cli.attributes.newest_version }} |
      | **DNS** | {{ states.sensor.updater_dns.attributes.current_version }} | | {{ states.sensor.updater_dns.attributes.newest_version }} |
      | **Multicast** | {{ states.sensor.updater_multicast.attributes.current_version }} | | {{ states.sensor.updater_multicast.attributes.newest_version }} |
      | **Observer** | {{ states.sensor.updater_observer.attributes.current_version }} | | {{ states.sensor.updater_observer.attributes.newest_version }} |

As I used many of the examples in this and other forums to build this card, I am not sure what is wrong with the code now. There are no errors in the logs. Just noting but the title showing on my screen. Any ideas on how to fix it?
Thanks.

what does it show in dev templates ?

Personally, I did away with most of these templates, and just added an auto-entities on domain update being on…

the more info HA integrations provide out of the box, the less we need to write our own investigator/monitor.