Ich überwache mein Zigbee-Netzwerk visuell (Linkquality & Last_seen)

Ziel: zwei wichtige Informationen für jedes Zigbee-Gerät sichtbar (und verwertbar) zu machen:

  • linkquality (Radio-Gesundheit) und
  • last_seen (letzte Mitteilung)

Dies, um sofort eine „verlorene“ Sirene oder HS-Batterie zu erkennen, ohne darauf zu warten, dass sie zufällig entdeckt wird.

🧭 Warum habe ich das eingerichtet?

Mit 69 Zigbee-Geräte, hatte ich keine einfache Sichtbarkeit, um zwei grundlegende Fragen zu beantworten: Nimmt das Gerät korrekt auf? und Hat er kürzlich mitgeteilt?
Auf einer Meerjungfrau, Es ist nicht akzeptabel, dies ‚zufällig‘ zu entdecken: Ich möchte sofort sehen, ob sie stumm, netzunabhängig oder in Funkschwierigkeiten ist.

Daher habe ich beschlossen, überall zu zeigen:

  • linkquality : Qualitätsindikator für Funkverbindungen (LQI). (Dies ist ein nützliches „Foto“, um schwache Bereiche zu erkennen.)
  • last_seen : Datum/Uhrzeit der letzten Mitteilung (oder relative Zeit: 12s, 4m, 2h…).



⚠gestützte Fallen #1 : Es gibt zwei ‚configuration.yaml‘

Am Anfang habe ich mich wie folgt verhalten: In Home Assistant gibt es zwei verschiedene Dateien.

  • Die Hauptdatei von Home Assistant: /config/configuration.yaml
  • Die Datei von Zigbee2MQTT: /config/zigbee2mqtt/configuration.yaml

Wichtig: für last_seen und (in der Praxis) die Standardbelichtung/-aktivierung, alles ist seitlich konfigurierbar Zigbee2MQTT, also in /config/zigbee2mqtt/configuration.yaml.

🧹 Falle #2 : Zigbee2MQTT-Add-on-UI kann Optionen „säubern“

Ich habe mich sehr peinlich verhalten: Ich fügte erweiterte Optionen hinzu, speicherte …, und dann schien die Schnittstelle des Add-Ons die Datei zu „vereinfachen“, indem sie Dinge löschte, die sie nicht verstand.

Zuverlässige Methode (bei mir):

  • Ich veröffentliche direkt /config/zigbee2mqtt/configuration.yaml (File Editor oder SSH)
  • Ich speichere
  • Ich starte Zigbee2MQTT neu

🕒 Aktivieren last_seen in Zigbee2MQTT (erforderlich)

Standardmäßig veröffentlicht Zigbee2MQTT möglicherweise nicht last_seen (‚disable‘-Wert).
Um es zu aktivieren, füge ich dies im Abschnitt advanced von /config/zigbee2mqtt/configuration.yaml :

advanced: # Fügt "last_seen" in MQTT-Nachrichten hinzu # Mögliche Werte: disable, ISO_8601, ISO_8601_local, epoch last_seen: ISO_8601_local

Diese Einstellung weist Zigbee2MQTT an, das Attribut last_seen MQTT-Nachrichten in einem Format, das in lokaler Zeit lesbar ist.

Nützliche Formate (zur Auswahl)

  • ISO_8601_local : lesbar + lokale Zeitzone (am bequemsten im Alltag).
  • ISO_8601 : lesbar, aber in UTC (nützlich, um alles in UTC zu zentralisieren).
  • Epoch : timestamp Unix (Praxis für Skripte/Verarbeitungen).

🧩 Aktivieren last_seen und linkquality „Standard“ im Home Assistant (optional)

Einmal last_seen Bei aktiviertem Zigbee2MQTT gibt es noch einen Punkt: In Home Assistant werden diese Entitäten häufig als „Diagnose“ eingestuft, sodass sie erstellt werden können, aber deaktiviert automatisch.

Damit jedes neu hinzugefügte Zigbee-Gerät direkt „zur Überwachung bereit“ ist, füge ich Folgendes hinzu: /config/zigbee2mqtt/configuration.yaml :

device_options: HomeAssistent: last_seen: enabled_by_default: true linkquality: enabled_by_default: true

Dann starte ich Zigbee2MQTT neu.

Wichtig: Auswirkungen auf bereits installierte Geräte

Diese Einstellung hilft vor allem den neue Geräte (oder wiederentdeckte Geräte).
Für bereits vorhandene Geräte müssen die entsprechenden Entitäten im Home Assistant oft manuell aktiviert werden.

🗄gestützte Datenbank: Info sichtbar halten, ohne die Historie zu „verschmutzen“

Das „Risiko“ ist nicht so sehr Zigbee2MQTT (es fügt nur ein Timestamp-Feld in Nachrichten hinzu), sondern eher Home Assistant, wenn jedes Update von last_seen in der Basis: Dies kann zu vielen Schreibvorgängen mit vielen Sensoren führen.

Meine Wahl: Ich will siehe Diese Werte sind überall vorhanden, aber ich brauche keine Graphen pro Minute. Ich schließe diese Einheiten daher vom Recorder aus.

Was ich in /config/recorders.yaml

Recorder: ausgenommen: entity_globs: - sensor.*_last_seen - sensor.*_linkquality

Danach starte ich Home Assistant neu: Die Werte bleiben in Echtzeit sichtbar, aber ich schränke die Erstellung unnötiger Historie stark ein.

🗺 Visuelle Überwachung: ein „Radio + Frische“-Plan

Ich wollte keine ‚in einer Liste verlorene Einrichtung‘: Ich wollte Visuelle Überwachung, sofort lesbar.

Grundsatz:

  • Jedes Zigbee-Gerät erscheint als kleines Rechteck auf einer Ebene.
  • Die angezeigte Zahl im Großen und Ganzen = Linkquality.
  • Die Zeit unten rechts = Last_seen relativ (s / m / h / d).
  • Schwarzer Rahmen = Router (variabel) Router: 1), um Router schnell zu erkennen.

🧰 Die Karte „decluttering“ (wiederverwendbar)

Ich nutze die Magie der Karte decluttering_templates, die ich vor 3 Jahren auf dieser Seite beschrieben habe:

Auf diese Weise decluttering_templates + custom:decluttering-card Sie können dieselbe Komponente überall wiederverwenden, indem Sie nur 4 Variablen ändern.

Ich definiere einfach 4 Elemente, die für jedes Gerät unterschiedlich sind:

  • Gebiet: Es ist der Sensor, der verarbeitet wird
  • top: Seine Position von oben
  • left: Seine Position von links
  • Router: 1 wenn es sich um einen Router handelt, ansonsten 0

Beispiel: Sehr einfache Dashboard-Seite

Typ: picture-elements image: /local/home/1920x1000/off_0800.png elements: - Typ: custom:decluttering-card template: linkquality Variablen: - Gebiet: sensor.lume_chambre_parents - top: 20% - left: 35% - Router: 1 - Typ: custom:decluttering-card template: linkquality Variablen: - Gebiet: sensor.fenetre_de_la_chambre_des_parents - top: 15% - left: 37% - Router: 0

Das Template linkquality (‚großes Stück‘)

decluttering_templates: linkquality: default: - top: 5% - left: 95% - Router: 0 element: Typ: custom:button-card entity: | [[return ¿[zone]]_linkquality¿; ]]] show_icon: false name: | [[ return '[[zone]]' .replace(/^sensor\./i, '') .replace(/_/g, ' ') .trim(); ]]] show_state: true show_units: false custom_fields: lastseen: | [[const obj = states[¿[[zone]]_last_seen¿]; if (!obj ||!obj.state || obj.state === 'unavailable') return '!!'; const last = new Date(obj.state); if (isNaN(last.getTime())) return '!!'; const now = new Date(); const seconds = Math.floor((now - last) / 1000); if (zweite < 60) return `${seconds}s¿; const minutes = Math.floor(seconds / 60); if (Minuten < 60) return `${Minuten}m′; const hours = Math.floor(Minuten / 60); if (hours < 24) return `${hours}h¿; const days = Math.floor(hours / 24); return `${days}d¿; ]]] Stile: Card: - Border: | [[Return ([Router]] == 1)? '2px solid black': 'none'; ]]] - box-shadow: none - Position: relativ - border-radius: 8px - min-width: 100px - height: 45px - Anzeige: flex - flex-direktion: column - justify-content: center - align-items: center - color: white name: - font-size: 10px - line-height: '1' - padding: 0 - Margin: 0 - text-align: center - color: white - flex: 1 - max-width: 95px - word-wrap: break-word - overflow: hidden - white-space: normal state: - font-size: 15px - font-weight: bold - line-height: '1' - padding: 0 - Margin: 0 - text-align: center - color: white - flex: 1 custom_fields: lastseen: - Position: Absolute - Bottom: 0px - right: 0px - font-size: 10px - color: black - padding: 2px 4px - border-radius: 4px - box-shadow: 0 1px 3px rgba(0,0,0,0.3) - Hintergrund: | [[const obj = states[¿[[zone]]_last_seen¿]; if (!obj ||!obj.state) return 'rgba(128,128,128,0.5)'; const last = new Date(obj.state); const now = new Date(); const seconds = Math.floor((now - last) / 1000); if (zweite < 1200) return 'rgba(0,255,0,0.9)'; // Grün <20min if (zweite < 1800) return 'rgba(255,255,0,0.9)'; // Gelb 20-30min if (zweite < 7200) return 'rgba(255,165,0,0.9)'; // Orange <2h return 'rgba(255,0,0,0.9)'; // Rot >2h ]]] state: - value: unavailable Stile: Card: - Hintergrund: rgba(255,0,0,0.3) - operator: '>' value: 200 Stile: Card: - Hintergrund: rgba(0,180,0,0.3) - operator: '>' value: 150 Stile: Card: - Hintergrund: rgba(34,255,34,0.3) - operator: '>' value: 130 Stile: Card: - Hintergrund: rgba(255,255,0,0.3) - operator: '>=' value: 80 Stile: Card: - Hintergrund: rgba(255,165,0,0.3) - operator: '<' value: 80 Stile: Card: - Hintergrund: rgba(255,107,53,0.3) Stil: top: '[[top]]' left: '[[left]]'

Was macht dieser Code eigentlich?

Dieses Template definiert eine einzigartige Kartenvorlage, die du für alle deine Zigbee-Geräte wiederverwendest, indem du nur die Variable [[zone]] änderst.

Die entity-Eigenschaft wird dynamisch in JavaScript ([[[… ]]]) erstellt, um auf die Entität [[zone]]_linkquality zu verweisen, wodurch die Linkqualität des Geräts automatisch angezeigt wird (LQI-Wert, der von Zigbee2MQTT oder ZHA abgerufen wird).

Auch der Name wird clientseitig generiert: Das Präfix sensor wird entfernt, dann werden die _ durch Leerzeichen ersetzt und ein trim() erstellt, was eine lesbare Beschriftung aus der Entitäts-ID ergibt.

Das benutzerdefinierte Feld lastseen liest die Entität [[zone]]_last_seen in states[], konvertiert ihren Wert in das Datumsobjekt und berechnet dann die Differenz zur aktuellen Zeit, um eine relative Zeit (s, m, h, d) anzuzeigen. Wenn der Wert leer, unavailable oder ungültig ist, zeigt er !! an, anstatt einen inkonsistenten Wert erscheinen zu lassen.

Der Stil des Lastseen-Badges unten rechts verwendet eine JavaScript-Funktion, die je nach Alter der letzten Kommunikation eine andere RGBA-Farbe zurückgibt (grau, wenn kein Wert, dann grün → gelb → orange → rot je nach Anzahl der verstrichenen Sekunden), wodurch die Frische der Daten visuell kodiert wird.

Die Sektion state: Die button-card legt mehrere Schwellenwerte für den LinkQuality-Wert fest: nicht verfügbar, dann Scheiben >200, >150, >130, >=80, <80, die jeweils einen kartenspezifischen farbigen Hintergrund anwenden, wodurch ein numerischer Wert in einen sofort lesbaren Radioqualitätsindikator umgewandelt wird.

Die Formatierungseigenschaften (flex, min-width, height, border-radius, absolute Badge-Position usw.) sorgen für eine kompakte und gleichmäßige Darstellung mit einem optionalen schwarzen Rand, wenn [[router]] == 1 ist, um Zigbee-Router im Vergleich zu einfachen Terminals visuell zu identifizieren.

🧠 Nützliche Ergänzungen: Globale Gesundheitsinformationen Zigbee2MQTT

Hier ist der Code dieser Karte

Typ: picture-elements image: /local/home/1920x1000/off_0800.png elements: - Typ: custom:mod-card card: Typ: entities title: Zigbee2MQTT show_header_toggle: false entities: - entity: switch.zigbee2mqtt_bridge_permit_join name: Aufnahme zulassen - entity: button.zigbee2mqtt_bridge_restart name: Neustart - entity: select.zigbee2mqtt_bridge_log_level name: Log-Level - Entity: binary_sensor.zigbee2mqtt_bridge_connection_state name: Verbindungsstatus - Typ: custom:template-entity-row name: Gestartet seit state: > {% set e = states.binary_sensor.zigbee2mqtt_bridge_connection_state %} {% if e is not none and e.state not in ['unavailable', 'unknown'] %} {{ ((now() - e.last_changed).total_seconds() / 3600) | round(1) }} h {% else %} Nicht verfügbar {% endif %} - entity: sensor.zigbee2mqtt_bridge_coordinator_version name: Koordinierende Version - entity: sensor.zigbee2mqtt_bridge_version name: Version Zigbee2MQTT - entity: sensor.number_de_Sensoren_linkq_en_Fehlername: Unerreichbare Icons: mdi:alert-circle - entity: sensor.number_de_Sensoren_linkq name: Gesamt-Icon: mdi:devices - entity: sensor.zigbee_linkquality_Mittelname: Durchschnittliche LinkQuality Icon: mdi:signal style: top: 35% left: 75% width: 450px

Ich habe einen Sensor hinzugefügt, um den Mittelwert von Linkquality zu berechnen.

Dieser Sensor wird in die Datei templates.yaml eingefügt, hier sein Code:

- name: "zigbee_linkquality_mittel"einzigartig_id: zigbee_linkquality_durchschnittlich unit_of_measurement: "LQI" state_class: measurement state: >- {% set linkqualities = states.sensor | selectattr('entity_id', 'search', '_linkquality') | selectattr('state', 'is_number') | map(attribute='state') | map('float') | list %} {% if linkqualities | length > 0 %} {{ (linkqualities | sum / linkqualities | length) | round(1) }} {% else %} 0 {% endif %}

Ich habe einen Sensor hinzugefügt, um die Anzahl der Sensoren zu berechnen

- name: Einzigartige _id "Anzahl der Linkq-Sensoren": sensor.number_sensoren_linkq state: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | list %} {{ lq_sensors | count }} icon: mdi:signal-variant unit_of_measurement: "Sensoren"

Ich habe einen Sensor hinzugefügt, um die Anzahl der fehlerhaften Geräte zu berechnen

- name: "Anzahl der Linkq-Sensoren irrtümlich" einzelne_id: sensor.number_sensoren_linkq_error state: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | selectattr('state', 'in', ['unavailable', 'unknown', 'None']) | list %} {{ lq_sensors | count }} icon: mdi:alert-circle unit_of_measurement: "Sensoren"

Ich habe einen Indikator auf dem Haupt-Dasboard hinzugefügt, um die falsche Zahl anzuzeigen

Zuerst wird dieses Template angezeigt, um xx falsch / yy insgesamt anzuzeigen

- name: "zigbee_injonable_synthese" einzigartig_id: sensor.zigbee_injoignables_synthese state: >- {{ states('sensor.number_de_sensors_linkq_en_error') }}/{{ states('sensor.number_de_sensors_linkq') }} icon: mdi:alert-circle

Und in meinem picture-elements füge ich hinzu:

- Typ: conditional Bedingungen: - Bedingung: State Entity: sensor.number_de_Sensoren_linkq_en_Fehler state_not: "0" Elemente: - Typ: custom:button-card entity: sensor.zigbee_injoignables_synthese icon: mdi:zigbee show_name: false show_state: True Style: top: 5% left: 5% transform: translate(-50%, -50%) Stile: state: - color: red - font-weight: 700 - font-size: 16px Icon: - color: red - width: 20px - height: 20px Karte: Border-Radius: 999px - padding: 4px 6px - Hintergrund: transparent - box-shadow: none

Dieser Block wird verwendet, um eine kleine rote Anzeige "Zigbee irrtümlich" auf dem Dashboard-Plan anzuzeigen nur wenn es mindestens einen problematischen Sensor gibt.

Es handelt sich um ein bedingtes Element in einer Picture-Elements-Karte: es zeigt seinen Inhalt nur an, wenn sensor.number_de_sensors_linkq_en_Fehler von "0" abweicht (wenn also mindestens ein Sensor nicht erreichbar oder LinkQuality-Fehler ist).

Wenn die Bedingung wahr ist, wird eine custom:button-card angezeigt, die auf sensor.zigbee_injoignables_synthese basiert und wahrscheinlich Text im Stil "3 irrtümlich" oder ähnlich enthält.

Die Schaltfläche befindet sich oben links auf der Ebene (5 % / 5 %), zentriert durch transform: translate(-50%, -50%), mit transparentem Hintergrund und ohne Schatten, um nur das Zigbee-Symbol und den Text zu behalten.

Das Zigbee-Symbol und der Status sind in roter, fetter und 16 px-Größe mit einem Border-radius gestaltet: 999px, um ein Pastillen-/Etikett-Look zu verleihen, wodurch es zu einer diskreten, aber gut sichtbaren visuellen Warnung wird, solange Sensoren irregeführt werden.

Um herauszufinden, wer falsch liegt

Um die Liste der irrtümlichen Geräte auf einen Blick zu sehen, finden Sie hier die erforderlichen Codes.

Die Deklaration der Templates

- name: "Anzahl der Linkq-Sensoren irrtümlich" einzelne_id: sensor.number_sensoren_linkq_error state: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | selectattr('state', 'in', ['unavailable', 'unknown', 'None']) | list %} {{ lq_sensors | count }} icon: mdi:alert-circle unit_of_measurement: "Sensoren" - name: "Linkquality Sensor List in Täuschung" einzelne_id: sensor.liste_sensoren_linkquality_error state: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | selectattr('state', 'in', ['unavailable', 'unknown', 'None']) | map(attribute='entity_id') | map('regex_replace', 'sensor\\\\\\\\.(.*)_linkquality$', '\\\\\\\\1') | list %} {% set count = lq_sensors | length %} {% if count == 0 %} Keine {% elif count <= 5 %} {{ lq_sensors | join(', ') }} {% else %} {{ lq_sensors[:5] | join(', ') ~ ' + ' ~ count }} {% endif %} Attribute: count: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | selectattr('state', 'in', ['unavailable', 'unknown', 'None']) | map(attribute='entity_id') | map('regex_replace', 'sensor\\\\\\\\.(.*)_linkquality$', '\\\\\\\\1') | list %} {{ lq_sensors | length }} full_list: >- {% set lq_sensors = states.sensor | selectattr('entity_id', 'search', '_linkquality$') | selectattr('state', 'in', ['unavailable', 'unknown', 'None']) | map(attribute='entity_id') | map('regex_replace', 'sensor\\\\\\\\.(.*)_linkquality$', '\\\\\\\\1') | list %} {{ lq_sensors | join(', ') | default('Keine') }} icon: mdi:alert-circle

Und auf dem Dasboard, immer im picture-elements

Typ: picture-elements image: /local/home/1920x1000/off_0800.png elements: - Typ: custom:mod-card card: Typ: entities title: Zigbee Sensoren irrtümlich entities: - Typ: custom:template-entity-row entity: sensor.liste_Sensoren_linkquality_en_Fehlername: Liste der Staaten: > {% set s = states('sensor.list_sensors_linkquality_en_error') %} {% if s not in ['unknown', 'unavailable', ''] %} {{ s.replace(',', '\\\\n') }} {% else %} Keine {% endif %} Stil: top: 80% left: 75% width: 450px

✅ Einfache Zusammenfassung (die 3 unentbehrlichen Blöcke)

1. Zigbee2MQTT: last_seen aktivieren

advanced: last_seen: ISO_8601_local

2. Zigbee2MQTT: Standardeinstellung in HA aktivieren (optional, aber praktisch)

device_options: HomeAssistent: last_seen: enabled_by_default: true linkquality: enabled_by_default: true

3. Home Assistent: diese Entitäten nicht in der DB registrieren

Recorder: ausgenommen: entity_globs: - sensor.*_last_seen - sensor.*_linkquality

🎯 Was sich im Alltag ändert

  • Ich sehe sofort schwache Bereiche (linkquality niedrig) und ich weiß, was zu stärken (Router, Platzierung, Kanal, etc.).
  • Ich weiß genau wenn eine Sirene aufgehört hat zu sprechen (last_seen), so „vermute“ ich nicht mehr.
  • Ich habe eine gesunde Datenbank (keine unnötige Historie auf Sensoren, die sich ständig bewegen)



Brauchen Sie weitere Informationen? - Fragen?

Diese Diskussion (falls noch offen) steht zur Verfügung:

https://forum.hacf.fr/t/retex-je-surveille-visuellement-mon-reseau-zigbee-linkquality-last-seen/76732