Skip to content

Instantly share code, notes, and snippets.

@Xoma163
Last active January 21, 2026 15:02
Show Gist options
  • Select an option

  • Save Xoma163/832ece3c2bc2934a1d8e94a83359c68e to your computer and use it in GitHub Desktop.

Select an option

Save Xoma163/832ece3c2bc2934a1d8e94a83359c68e to your computer and use it in GitHub Desktop.
Home Assistant MQTT Last‑Seen Monitor (Markdown Card)
type: markdown
entity_id: sensor.every_5s
content: |-
  {# Параметры #}
  {% set warn_h = 2 %}
  {% set crit_h = 3 %}

  {% set COL_NAME = 'Device' %}
  {% set COL_STATUS = 'Status' %}
  {% set COL_SINCE = 'Seen' %}

  {# Бейджи #}
  {% set BADGE_OK = '🟩' %}
  {% set BADGE_WARN = '🟧' %}
  {% set BADGE_CRIT = '🟥' %}
  {% set BADGE_NONE = '⬛️' %}

  {# Данные #}
  {% set now_ts = as_timestamp(now()) %}
  {% set ns = namespace(items=[], ok=0, warn=0, crit=0, none=0) %}

  {% set mqtt_ents = integration_entities('mqtt') %}
  {% for s in states.sensor
        | selectattr('entity_id','search','_last_seen')
        | selectattr('entity_id','in', mqtt_ents) %}

    {% set ts = as_timestamp(s.state, none) %}
    {% if ts is none %}
      {% set badge = BADGE_NONE %}
      {% set since_txt = '—' %}
      {% set sort_key = -1 %}
      {% set ns.none = ns.none + 1 %}
    {% else %}
      {% set delta = (now_ts - ts) if (now_ts - ts) > 0 else 0 %}
      {% set h = (delta // 3600) | int %}
      {% set m = ((delta % 3600) // 60) | int %}
      {% set sec = (delta % 60) | int %}
      {% set since_txt = '%02d'|format(h) ~ ':' ~ '%02d'|format(m) ~ ':' ~ '%02d'|format(sec) %}
      {% set hours = delta / 3600 %}
      {% set badge = BADGE_CRIT if hours >= crit_h
                    else (BADGE_WARN if hours >= warn_h
                    else BADGE_OK) %}
      {% set sort_key = (1012 - ts|int) %} {# свежие выше #}
      {% if badge == BADGE_OK %}{% set ns.ok = ns.ok + 1 %}
      {% elif badge == BADGE_WARN %}{% set ns.warn = ns.warn + 1 %}
      {% else %}{% set ns.crit = ns.crit + 1 %}{% endif %}
    {% endif %}

    {% set did = device_id(s.entity_id) %}
    {% set dev_name = device_attr(did, 'name') if did else none %}
    {% set name = dev_name or s.name or s.entity_id %}

    {% set row = {
      'sort': sort_key,
      'html': '<tr><td>' ~ name ~ '</td>'
              ~ '<td><center>' ~ badge ~ '</center></td>'
              ~ '<td>' ~ since_txt ~ '</td></tr>'
    } %}
    {% set ns.items = ns.items + [row] %}
  {% endfor %}

  {% set rows = ns.items
      | sort(attribute='sort')
      | map(attribute='html')
      | join('') %}

  <table>
    <thead>
      <tr>
        <th>{{ COL_NAME }}</th>
        <th>{{ COL_STATUS }}</th>
        <th>{{ COL_SINCE }}</th>
      </tr>
    </thead>
    <tbody>
      {{ rows | safe }}
    </tbody>
  </table>

  <p>
    {{ BADGE_OK }} < {{ warn_h }}h ·
    {{ BADGE_WARN }} {{ warn_h }}–{{ crit_h }}h ·
    {{ BADGE_CRIT }} ≥ {{ crit_h }}h ·
    {{ BADGE_NONE }} no data
  </p>

@Xoma163
Copy link
Author

Xoma163 commented Jan 16, 2026

  1. In Zigbee2MQTT, go to Settings -> Settings -> Advanced, and set last_seen to ISO_8601_local.
  2. Restart Zigbee2MQTT.
  3. In Home Assistant: Settings -> Devices & Services -> Entities. Filter by integration = MQTT and status = Disabled (Deactivated), select all, and Activate.
  4. Restart Home Assistant.

For each new device, you need to enable it in Home Assistant. (p3)

@Xoma163
Copy link
Author

Xoma163 commented Jan 16, 2026

You can also update on a timer.
To do this, add to configuration.yaml:

template:
  - trigger:
      - platform: time_pattern
        seconds: "/5"
    sensor:
      - name: "every_5s"
        state: "{{ now().isoformat() }}"

And in the Markdown card, specify it like this:

type: markdown
entity_id: sensor.every_5s
content: |-
  ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment