OpenWrt - Webhook: curl and cron

Mit einem simplen Cron-Job (Scheduled Tasks) auf einem Openwrt Access-Point, kann alles, was das Linux-Terminal zur Verfügung stellt an Home-Assistant übertragen werden. Als Beispiel stellen meine OpenWrt-Geräte folgende Informationen zur Verfügung:
- Die Liste aller verbundenen Clients.
- CPU- und Memory-Auslastung.
- Der letzte Eintrag im Kernel-Log.
- Die DHCP-Leases vom Hauptrouter.
- Usteer-Daten vom Hauptrouter für die Netzwerkübersicht.
Der Cron-Job startet jede Minute und ermöglicht eine kontinuierliche Überwachung und Verwaltung der Netzwerkinfrastruktur über Home-Assistant.
Grundlagen für diese Umsetzung, siehe: Daten von beliebigen Linux-Systemen in HA anzeigen.
Voraussetzung auf den Access-Points: curl
Damit die Access-Points Daten an einen Webhook in Home Assistant senden können, wird einzig das kleine Softwarepaket "curl" benötigt:
Nach der Installation von curl können beliebige Befehle zum Beispiel über Scheduled Tasks im Minutentakt an Home Assistant übertragen werden:
Scheduled Tasks
Auf den Accesspoints verwende ich folgende Befehle in den Scheduled Tasks für das Sammeln der Daten. Die Befehle können beliebig angepasst oder um weitere Befehle erweitert werden. Dazu macht es Sinn, die Befehle vorher in einem Terminal auszutesten.
* * * * * log=$(dmesg | tail -1) && \
uptime=$(uptime) && \
stations=$(iw phy1-ap1 station dump && iw phy1-ap0 station dump && iw phy0-ap0 station dump) && \
free=$(free | grep Mem | awk '{print $4/$2 * 100.0}') && \
curl -X POST \
-d log="$log" \
-d uptime="$uptime" \
-d stations="$stations" \
-d free="$free" \
http://192.168.1.5:8123/api/webhook/MySuperSecret-$HOSTNAME
Das OpenWrt-Gerät mit aktiven DHCP habe ich um zusätzliche Befehle erweitert, damit das Gerät die DHCP-Leases überträgt und zusätzlich die Usteer-Daten aller verbundenen Clients:
* * * * * dhcp=$( cat /tmp/dhcp.leases| awk '{print $2,$4,$1}' | sed 's/ \+/\_/g') && \
log=$(dmesg | tail -1) && \
usteer=$(ubus call usteer get_clients) && \
uptime=$(uptime) && \
stations=$(iw dev | grep -o "phy[0-9]-ap[0-9]*" | while read iface; do iw dev $iface station dump;done) && \
free=$(free | grep Mem | awk '{print $4/$2 * 100.0}') && \
curl -X POST \
-d dhcp="$dhcp" \
-d log="$log" \
-d uptime="$uptime" \
-d free="$free" \
-d stations="$stations" \
-d usteer="$usteer" http://192.168.1.5:8123/api/webhook/MySuperSecret-$HOSTNAME
Damit der Befehlsblock auf allen Access-Point ohne Anpassung eingesetzt werden kann, nutze ich für das Webhook-Secret zusätzlich den Hostnamen. Dadurch kann ich für alle Access Points das gleiche Secret und in Home-Assistant für jedes Gerät dennoch einen eigenen Webook verwenden. Das Client-Secret "???" und die IP-Adresse von Home Assistant müssen natürlich entsprechend angepasst werden, wobei das Client-Secret frei gewählt werden. Die Kombination aus Secret und Hostnamen aktiviert den jeweiligen Trigger und stellt die übertragenen Daten in dessen Automation zur Verfügung.
Empfangen der Daten in Home Assistant:
Damit auch Daten größer 255 Zeichen verarbeitet werden können, was für die DHCP-Leases und Usteer-Daten der Fall ist, setze ich die HACS-Integration "Variables+History" ein, siehe: Daten von beliebigen Linux-Systemen in HA anzeigen
Für den Empfang habe ich folgende Sensoren angelegt:
Für den Hauptrouter:
- input_text.openwrt_log (letzte Logzeile des Kernel Log, Standard Helper Sensor)
- sensor.openwrt_load (CPU Auslastung, Variables+History Sensor)
- sensor.openwrt_ram (RAM Auslastung, Variables+History Sensor)
- sensor.openwrt_crondata (DHCP und Usteer Daten, Variables+History Sensor)
und für die Access Points:
- input_text.APNAME_log (Standard Helper Sensor)
- sensor.APNAME_load (Variables+History Sensor)
- sensor.APNAME_ram (Variables+History Sensor)
Die Automation für das Befüllen der Sensoren sieht wie folgt aus:
alias: WebHook-OpenWrt
description: ""
triggers:
- trigger: webhook
allowed_methods:
- POST
- PUT
local_only: true
webhook_id: "???-OpenWrt"
conditions: []
actions:
- action: input_text.set_value
metadata: {}
data:
value: "{{ trigger.data.log }}"
target:
entity_id:
- input_text.openwrt_log
- action: variable.update_sensor
metadata: {}
data:
replace_attributes: true
attributes:
dhcp: "{{ trigger.data.dhcp }}"
usteer: "{{ trigger.data.usteer }}"
stations: "{{ trigger.data.stations }}"
value: >-
{% set Stations = trigger.data.stations.split('Station ') %} {% for
Station in Stations if (Station.split("\n")[0].split(" (")[0] != "")%}
{% set StationDetail = Station.split("\n") %}{% set mac =
StationDetail[0].split(" (")[0] %}{{ mac.replace(":","") }}{% endfor %}
target:
entity_id: sensor.openwrt_crondata
- action: variable.update_sensor
metadata: {}
data:
replace_attributes: false
value: "{{ trigger.data.uptime.split(\": \")[1].split(\",\")[0] }}"
target:
entity_id: sensor.openwrt_load
- action: variable.update_sensor
metadata: {}
data:
replace_attributes: false
value: "{{100 - trigger.data.free | float}}"
target:
entity_id: sensor.openwrt_ram
mode: parallel
max: 10
Die Automation der Access-Points ist gleich, mit Ausnahme der DHCP und Usteer-Daten.
Dashboard
Für jeden der Access-Points habe ich dann folgende Markdown-Card angelegt:
{% set dhcplist = states.sensor.openwrt_crondata.attributes.dhcp.split('\n') %}
{% set Stations = states.sensor.openwrt_crondata.attributes.stations.split('Station ') %}
{% set DetailsColumns = (1,9,11,13)%}
{% if (Stations | count > 1) %}
<table><tr><th>Lease</th><th>Device</th><th>Wifi</th><th>Details</th> </tr>{%set fontcolor="999"%}
{% for Station in Stations if (Station.split("\n")[0].split(" (")[0] != "")%}
{% set StationDetail = Station.split("\n") %}
{% set mac = StationDetail[0].split(" (")[0] %}
{% set dev = StationDetail[0].split(" (")[1].replace("on phy1-ap1)","2,4G").replace("on phy1-ap0)","2,4G").replace("on phy0-ap0)","5G").replace("on phy0-ap1)","5G") %}
{% set match = dhcplist | select('match', mac) | list%}
{%if match %}
{% set match = match[0].split("_") %}
{% if(StationDetail[1].split(":")[1].replace("ms","") | int > 1000) %}{%set fontcolor="999"%}{% else%}{%set fontcolor="fff"%}{% endif%}
<tr><td><font color="#{{fontcolor}}"> {{(now().timestamp() | int - (match[2] | int) + 43200 ) | timestamp_custom('%H:%M',false) }} </td><td><font color="#{{fontcolor}}"> {{match[1]}}<br><small>{{mac}}</small></td>
{% else %}
<tr><td>-</td><td><small>{{mac }}</small></td>
{% endif %}
<td><font color="#{{fontcolor}}"> {{dev}}
</td><td><font color="#{{fontcolor}}"> {% for DetailsColumn in DetailsColumns %} {{StationDetail[DetailsColumn].split(":")[0]}}:{{StationDetail[DetailsColumn].split(":")[1]}}<br> {% endfor %}
</td>
</tr>
{% endfor %}
</table>
{% else %}
keine Verbindungen ..
{% endif %}
Für jeden AccessPoint muss natürlich der Sensorname, hier "sensor.openwrt_crondata" vorab angelegt und hier angepasst werden.
Zudem können die Zähler für CPU und RAM für die Charts verwendet werden:
Fazit
Die Nutzung von Webhooks in Verbindung mit curl und cron ermöglicht eine effektive Überwachung von Linux-basierten Netzwerksystemen in Home Assistant. Durch regelmäßige Übertragung von Netzwerkdaten wie CPU- und RAM-Auslastung, verbundene Clients und DHCP-Leases bleibt die Netzwerkinfrastruktur stets aktuell. Die Implementierung ist einfach und erfordert lediglich die Installation von curl auf den Access-Points.

{{percentage}} % positiv
