Inhaltsverzeichnis
openwrtone: Skripte und Anpassungen (Quelltexte)
Stand 10.10.2026. Alle Eigenbauten des Routers openwrtone (10.10.11.1, OpenWrt 25.12.5) mit Erklärung. Sie stehen alle in /etc/sysupgrade.conf und überstehen ein Update; das Skript anpassungen-anwenden stellt die Änderungen an Systemdateien nach einem Update wieder her. Passwörter und Webhook-IDs sind hier nicht enthalten (Webhook-ID im Watchdog als <WEBHOOK-ID> ersetzt, die anderen stehen in /etc/tunnel-hook und /etc/landal-hook auf dem Router).
Übersicht:
| Datei | Aufgabe |
|---|---|
/usr/sbin/uplink-watchdog | wählt den Uplink (Huawei-Stick oder Landal-WLAN) und setzt die Route zum Tunnel-Endpunkt |
/usr/sbin/tunnel-failover | schaltet zwischen Tunnel zur FRITZ!Box, Tunnel zum Proxmox und direktem Internet |
/usr/sbin/uplink-test-run + CGI uplink-test | manueller Geschwindigkeitstest beider Uplinks, Ergebnis wird übernommen |
/usr/sbin/anpassungen-anwenden | spielt die Änderungen an OpenWrt-Systemdateien (Landal-VHT-Abschaltung) nach Updates wieder ein |
/usr/sbin/landal-vergleich + CGI | misst HT gegen VHT am Landal-Client und schreibt die CSV |
/usr/sbin/landal-auswertung | wertet die CSV aus |
Cron, supplicant.uc-Patch | zeitgesteuerte Messung, täglicher Neustart, Landal erzwingt HT |
Uplink-Watchdog
Läuft als Dienst (procd). Prüft alle 10 s beide Uplinks per Ping, misst gelegentlich die Geschwindigkeit und setzt die Host-Route zum Tunnel-Endpunkt über den besseren Uplink. /tmp/uplink-mode (auto|huawei|landal) erzwingt einen Uplink, /tmp/uplink-test.pref enthält das Ergebnis des manuellen Tests. Meldet Wechsel per Webhook an HA Arcen.
- /usr/sbin/uplink-watchdog
#!/bin/sh # Haelt den WireGuard-Tunnel "fritzbox" auf dem besseren Uplink. # Regel (seit 09.10.2026): Landal (wwan2), solange es schneller ist; Huawei (wwan), # wenn Landal langsamer als der Huawei ist oder kein Internet hat. Faellt einer aus, # laeuft der Tunnel ueber den anderen. # Der gesamte LAN-Verkehr laeuft durch den Tunnel, deshalb genuegt es, die Route # zum Tunnel-Endpunkt umzulegen. # Zustand: cat /tmp/uplink-watchdog.state Log: logread -e uplink-watchdog . /lib/functions/network.sh WG=fritzbox HUAWEI=wwan LANDAL=wwan2 TARGETS="1.1.1.1 8.8.8.8 9.9.9.9" INTERVAL=10 DOWN_AFTER=3 # so viele Fehlversuche in Folge, dann gilt ein Uplink als gestoert UP_AFTER=6 # so viele Erfolge in Folge, dann gilt er wieder als in Ordnung RESOLVE_EVERY=30 STATE=/tmp/uplink-watchdog.state # Vorgabe aus Home Assistant (auto|huawei|landal), geschrieben von /www/cgi-bin/uplink-mode. # Fehlt die Datei (z. B. nach einem Neustart), gilt auto. MODEFILE=/tmp/uplink-mode # Home Assistant Arcen bekommt den aktuellen Uplink gemeldet (Automation "System: Router-Uplink uebernehmen") HA_HOOK=http://10.10.11.12/api/webhook/router-uplink-<WEBHOOK-ID> # Geschwindigkeitsvergleich (braucht das Paket curl; ohne curl bleibt es bei Landal, solange es Internet hat) SPEED_HOST="http://speed.cloudflare.com/__down?bytes=" SPEED_EVERY=90 # alle 90 Durchlaeufe = 15 Minuten PROBE_BYTES=1000000 # Landal allein: 1 MB (kostet kein Huawei-Volumen) CMP_BYTES=300000 # Vergleich beider Leitungen: je 300 kB FAST_KBS=1000 # ist Landal schneller als 1000 kB/s (8 Mbit/s), wird der Huawei nur HUAWEI_EVERY=4 # bei jeder 4. Messung (stuendlich) mitgemessen, das spart Volumen: # ca. 7 MB pro Tag, sonst (Landal langsam) ca. 29 MB pro Tag EP_HOST=$(uci -q get network.@wireguard_$WG[0].endpoint_host) EP_PORT=$(uci -q get network.@wireguard_$WG[0].endpoint_port) log() { logger -t uplink-watchdog "$*"; } check() { local t for t in $TARGETS; do ping -c 1 -W 2 -I "$1" "$t" >/dev/null 2>&1 && return 0 done return 1 } # speed <geraet> <bytes>: Download-Geschwindigkeit in kB/s direkt ueber dieses Geraet (am Tunnel vorbei) speed() { local s command -v curl >/dev/null || return 1 s=$(curl -s -m 10 --interface "$1" -o /dev/null -w '%{speed_download}' "$SPEED_HOST$2" 2>/dev/null) s=${s%%.*} case "$s" in ''|*[!0-9]*) return 1 ;; esac echo $((s / 1000)) } # track <h|l> <geraet> <gateway> <name>: Erreichbarkeit eines Uplinks mit Entprellung fuehren track() { local up fails oks eval "up=\$${1}_up; fails=\$${1}_fails; oks=\$${1}_oks" if [ -n "$2" ] && [ -n "$3" ] && check "$2"; then fails=0 oks=$((oks + 1)) if [ $up = 0 ] && [ $oks -ge $UP_AFTER ]; then up=1; log "$4 hat wieder Internet"; fi else oks=0 fails=$((fails + 1)) if [ $up = 1 ] && [ $fails -ge $DOWN_AFTER ]; then up=0; log "$4 ohne Internet"; fi fi eval "${1}_up=$up; ${1}_fails=$fails; ${1}_oks=$oks" } h_up=1; h_fails=0; h_oks=0 l_up=1; l_fails=0; l_oks=0 pref=landal sp=0 hkbs="" lkbs="" sent="" cur="" mode=auto ep_ip="" n=0 # Ergebnis des Knopfs "Uplinks jetzt testen" (geschrieben von /usr/sbin/uplink-test-run) TESTFILE=/tmp/uplink-test.pref TEST_HOLD=1800 # so lange (s) nach einem Test macht der Router keine eigene Messung testnote="" testtime=0 while :; do hdev=""; hgw=""; ldev=""; lgw="" network_flush_cache network_get_device hdev $HUAWEI network_get_gateway hgw $HUAWEI network_get_device ldev $LANDAL network_get_gateway lgw $LANDAL track h "$hdev" "$hgw" Huawei track l "$ldev" "$lgw" Landal # Ergebnis eines von Hand gestarteten Tests uebernehmen if [ -r $TESTFILE ]; then T_PREF=""; T_H=""; T_L=""; T_NOTE="" . $TESTFILE rm -f $TESTFILE if [ -n "$T_PREF" ]; then pref=$T_PREF [ -n "$T_H" ] && hkbs=$T_H [ -n "$T_L" ] && lkbs=$T_L testnote=$T_NOTE; testtime=$(date +%s) log "Test: bevorzugt jetzt $pref (Landal ${lkbs:-?} kB/s, Huawei ${hkbs:-?} kB/s)" fi fi # Geschwindigkeit: Landal jedes Mal (kostenlos), Huawei stuendlich oder wenn Landal nicht klar schnell ist if [ $((n % SPEED_EVERY)) = 3 ] && [ $(( $(date +%s) - testtime )) -ge $TEST_HOLD ] && command -v curl >/dev/null; then was=$pref sp=$((sp + 1)) l=""; h="" if [ $l_up = 1 ] && [ -n "$lgw" ]; then l=$(speed "$ldev" $PROBE_BYTES) || l=0; fi if [ $h_up = 1 ] && [ -n "$hgw" ]; then if [ -z "$l" ] || [ "$l" -lt $FAST_KBS ] || [ -z "$hkbs" ] || [ $((sp % HUAWEI_EVERY)) = 0 ]; then h=$(speed "$hdev" $CMP_BYTES) || h=0 fi fi if [ -n "$l" ] && [ -n "$h" ]; then # knapper Fall: Landal mit derselben Dateigroesse messen wie den Huawei if [ "$l" -lt $FAST_KBS ] && l2=$(speed "$ldev" $CMP_BYTES); then l=$l2; fi if [ $pref = landal ]; then # erst wechseln, wenn der Huawei deutlich (20 %) schneller ist [ "$h" -gt $((l * 12 / 10)) ] && pref=huawei else [ "$l" -gt "$h" ] && pref=landal fi fi [ -n "$l" ] && lkbs=$l [ -n "$h" ] && hkbs=$h [ $pref != $was ] && log "Bevorzugt jetzt $pref (Landal ${lkbs:-?} kB/s, Huawei ${hkbs:-?} kB/s)" fi # Vorgabe aus Home Assistant: fester Uplink oder auto. Auch bei fester Vorgabe gilt: # hat der gewaehlte Uplink kein Internet, laeuft der Tunnel ueber den anderen. m=auto [ -r $MODEFILE ] && read m < $MODEFILE case "$m" in huawei|landal) ;; *) m=auto ;; esac if [ "$m" != "$mode" ]; then log "Vorgabe jetzt: $m"; mode=$m; fi case $mode in huawei) order="h l" ;; landal) order="l h" ;; *) if [ $pref = landal ]; then order="l h"; else order="h l"; fi ;; esac use="" for x in $order; do eval "xup=\$${x}_up; xdev=\$${x}dev; xgw=\$${x}gw" if [ $xup = 1 ] && [ -n "$xgw" ]; then use=$x; dev=$xdev; gw=$xgw; break; fi done if [ -z "$use" ]; then # keiner gilt als in Ordnung: nimm den ersten, der ueberhaupt verbunden ist for x in $order; do eval "xdev=\$${x}dev; xgw=\$${x}gw" if [ -n "$xgw" ]; then use=$x; dev=$xdev; gw=$xgw; break; fi done fi peer=$(wg show $WG peers 2>/dev/null | head -n 1) # Endpunkt-Adresse der FRITZ!Box (MyFRITZ) regelmaessig neu aufloesen if [ -z "$ep_ip" ] || [ $((n % RESOLVE_EVERY)) = 0 ]; then new=$(resolveip -4 -t 5 "$EP_HOST" 2>/dev/null | head -n 1) [ -z "$new" ] && [ -z "$ep_ip" ] && new=$(wg show $WG endpoints 2>/dev/null | head -n 1 | cut -f 2 | cut -d : -f 1) if [ -n "$new" ] && [ "$new" != "$ep_ip" ]; then [ -n "$ep_ip" ] && ip route del "$ep_ip/32" 2>/dev/null ep_ip=$new [ -n "$peer" ] && wg set $WG peer "$peer" endpoint "$ep_ip:$EP_PORT" log "Tunnel-Endpunkt ist $ep_ip" fi fi n=$((n + 1)) if [ -n "$use" ] && [ -n "$ep_ip" ]; then case "$(ip -4 route show "$ep_ip/32" | head -n 1)" in *"via $gw dev $dev"*) ;; *) ip route replace "$ep_ip/32" via "$gw" dev "$dev" # alte NAT-Zuordnung des Tunnels verwerfen, sonst geht er mit falscher Absenderadresse raus echo "$ep_ip" > /proc/net/nf_conntrack 2>/dev/null ;; esac if [ "$use" != "$cur" ]; then log "Tunnel laeuft jetzt ueber $([ $use = h ] && echo Huawei || echo Landal) ($dev, Gateway $gw)" cur=$use fi fi case "$cur" in l) label=Landal if [ $mode = landal ]; then grund="Manuell eingestellt" elif [ $mode = auto ] && [ $pref = landal ]; then grund="Landal schneller" elif [ $h_up = 0 ]; then grund="Huawei ohne Internet" else grund="Huawei nicht verbunden" fi ;; h) label=Huawei if [ $mode = huawei ]; then grund="Manuell eingestellt" elif [ $mode = auto ] && [ $pref = huawei ]; then grund="Landal langsamer" elif [ -z "$lgw" ]; then grund="Landal nicht verbunden" else grund="Landal ohne Internet" fi ;; *) label="Kein Uplink" grund="Router hat keine Verbindung" ;; esac if [ -n "$testnote" ]; then if [ $(( $(date +%s) - testtime )) -lt $TEST_HOLD ]; then grund="$grund ($testnote)"; else testnote=""; fi fi hs=$(wg show $WG latest-handshakes 2>/dev/null | head -n 1 | cut -f 2) now=$(date +%s) { echo "uplink=$label" echo "vorgabe=$mode" echo "bevorzugt=$pref" echo "huawei_internet=$h_up" echo "landal_internet=$l_up" echo "huawei_kbs=${hkbs:--}" echo "landal_kbs=${lkbs:--}" echo "grund=$grund" echo "endpunkt=$ep_ip" echo "handshake_vor_s=$([ -n "$hs" ] && [ "$hs" != 0 ] && echo $((now - hs)) || echo -)" echo "geprueft=$(date '+%d.%m.%Y %H:%M:%S')" } > $STATE.tmp && mv $STATE.tmp $STATE # bei Aenderung sofort an Home Assistant melden, sonst alle 5 Minuten zur Auffrischung msg="$label|$grund|$hkbs|$lkbs" if [ "$msg" != "$sent" ] || [ $((n % RESOLVE_EVERY)) = 0 ]; then wget -q -T 5 -O /dev/null --post-data="uplink=$(echo "$label" | tr ' ' '+')&grund=$(echo "$grund" | tr ' ' '+')&huawei=$hkbs&landal=$lkbs" "$HA_HOOK" 2>/dev/null && sent=$msg fi sleep $INTERVAL done
Init-Skript Uplink-Watchdog
Startet den Watchdog beim Booten.
- /etc/init.d/uplink-watchdog
#!/bin/sh /etc/rc.common # Uplink-Umschaltung Huawei/Landal fuer den WireGuard-Tunnel START=99 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/sbin/uplink-watchdog procd_set_param respawn 3600 5 0 procd_set_param stderr 1 procd_close_instance }
Tunnel-Failover
Drei Stufen: A = WireGuard zur FRITZ!Box, B = WireGuard zum Proxmox (pve), C = direkt ins Internet ohne VPN. Mit Hysterese (3 Fehlschläge → abwärts, 6 Erfolge → zurück zur besseren Stufe). Prüft über die jeweiligen Policy-Routing-Tabellen (100/101). Zustand in /tmp/tunnel-failover.state, Meldung per Webhook (/etc/tunnel-hook) an HA.
- /usr/sbin/tunnel-failover
#!/bin/sh # Tunnel-Umschaltung fuer den Router in Arcen (angelegt 10.10.2026). # # Stufe A: Tunnel "fritzbox" (FRITZ!Box Raderbroich) = Normalbetrieb # Stufe B: Tunnel "pve" (WireGuard-Server in Proxmox) = wenn A nicht geht # Stufe C: direkt ins Internet ueber Huawei/Landal, ohne VPN = wenn weder A noch B gehen # Zurueck zu A bzw. B, sobald der Tunnel wieder stabil geht (6 Pruefungen in Folge, ca. 60 s). # # Geprueft wird je Tunnel per Ping durch genau diesen Tunnel (Policy-Routing nach Quelladresse), # unabhaengig davon, welche Stufe gerade aktiv ist: # fritzbox: 192.168.192.1 (FRITZ!Box) und 1.0.0.1 (Internet) mit Quelle 10.10.11.1 # pve: 10.10.20.1 (Server) und 1.0.0.1 (Internet) mit Quelle 10.10.20.2 # Aufruf mit DRY=1 schreibt nur ins Log und aendert keine Routen. WG1=fritzbox WG2=pve SRC1=10.10.11.1 SRC2=10.10.20.2 LAN_PROBE=192.168.192.1 NET_PROBE=1.0.0.1 SRV_PROBE=10.10.20.1 INTERVAL=10 FAIL_N=3 OK_N=6 PVE_PORT=51820 STATE=/tmp/tunnel-failover.state SAVED=/tmp/tunnel-failover.routes UPSTATE=/tmp/uplink-watchdog.state DRY=${DRY:-0} log() { logger -t tunnel-failover "$*"; echo "$(date +%H:%M:%S) $*"; } ping_via() { ping -c1 -W2 -I "$1" "$2" >/dev/null 2>&1; } ensure_rules() { ip rule | grep -q "from $SRC1 to $LAN_PROBE lookup 100" || ip rule add from $SRC1 to $LAN_PROBE lookup 100 priority 100 ip rule | grep -q "from $SRC1 to $NET_PROBE lookup 100" || ip rule add from $SRC1 to $NET_PROBE lookup 100 priority 101 ip rule | grep -q "from $SRC2 lookup 101" || ip rule add from $SRC2 lookup 101 priority 102 ip route replace default dev $WG1 table 100 2>/dev/null ip route replace default dev $WG2 table 101 2>/dev/null } save_routes() { # Routen des FRITZ!Box-Tunnels merken, solange Stufe A aktiv ist r=$(ip route show dev $WG1 | grep -v "proto kernel") [ -n "$r" ] && echo "$r" > $SAVED } restore_routes() { [ -f $SAVED ] || return while read -r line; do [ -n "$line" ] && ip route replace $line dev $WG1 2>/dev/null done < $SAVED } drop_default() { ip route del default dev $1 2>/dev/null; ip route del default via "$2" dev "$3" metric 5 2>/dev/null; } direct_gw() { # Uplink, den uplink-watchdog gerade bevorzugt (Landal oder Huawei) u=$(grep '^uplink=' $UPSTATE 2>/dev/null | cut -d= -f2) case "$u" in Landal) DDEV=phy1-sta0;; *) DDEV=phy0-sta0;; esac DGW=$(ip route show default dev $DDEV 2>/dev/null | awk '/via/{print $3; exit}') if [ -z "$DGW" ]; then # anderen Uplink versuchen [ "$DDEV" = phy1-sta0 ] && DDEV=phy0-sta0 || DDEV=phy1-sta0 DGW=$(ip route show default dev $DDEV 2>/dev/null | awk '/via/{print $3; exit}') fi } apply_mode() { # $1 = A|B|C, $2 = LAN-Sonde ueber fritzbox ok (1/0) [ "$DRY" = 1 ] && return case "$1" in A) ip route del default dev $WG2 2>/dev/null ip route del 192.168.192.0/24 dev $WG2 2>/dev/null direct_gw; [ -n "$DGW" ] && ip route del default via "$DGW" dev "$DDEV" metric 5 2>/dev/null ip route show default dev $WG1 | grep -q . || restore_routes save_routes ;; B) # FRITZ!Box-Tunnel entlasten, Standard und Raderbroich-LAN ueber pve ip route del default dev $WG1 2>/dev/null ip route del 192.168.192.0/24 dev $WG1 2>/dev/null direct_gw; [ -n "$DGW" ] && ip route del default via "$DGW" dev "$DDEV" metric 5 2>/dev/null ip route replace default dev $WG2 2>/dev/null ip route replace 192.168.192.0/24 dev $WG2 2>/dev/null ;; C) ip route del default dev $WG1 2>/dev/null ip route del default dev $WG2 2>/dev/null ip route del 192.168.192.0/24 dev $WG2 2>/dev/null # Raderbroich-LAN ueber den FRITZ!Box-Tunnel nur behalten, wenn er das LAN noch erreicht if [ "$2" = 1 ]; then ip route replace 192.168.192.0/24 dev $WG1 proto static scope link 2>/dev/null; else ip route del 192.168.192.0/24 dev $WG1 2>/dev/null; fi direct_gw [ -n "$DGW" ] && ip route replace default via "$DGW" dev "$DDEV" metric 5 ;; esac } HOOKFILE=/etc/tunnel-hook report() { [ "$DRY" = 1 ] && return [ -f $HOOKFILE ] || return H=$(cat $HOOKFILE) wget -q -T 6 -O /dev/null --post-data="stufe=$mode&info=fritzbox:$f_state,+pve:$p_state" "$H" 2>/dev/null } sync_pve_endpoint() { [ "$DRY" = 1 ] && return ep=$(wg show $WG1 endpoints 2>/dev/null | head -n 1 | cut -f2 | cut -d: -f1) pk=$(wg show $WG2 peers 2>/dev/null | head -n 1) cur=$(wg show $WG2 endpoints 2>/dev/null | head -n 1 | cut -f2 | cut -d: -f1) [ -n "$ep" ] && [ -n "$pk" ] && [ "$ep" != "$cur" ] && wg set $WG2 peer "$pk" endpoint "$ep:$PVE_PORT" && log "Endpunkt $WG2 auf $ep gesetzt" } cleanup() { log "beendet, stelle Stufe A her"; [ "$DRY" = 1 ] || apply_mode A 1; exit 0; } trap cleanup TERM INT f_state=up; p_state=down f_bad=0; f_good=0; p_bad=0; p_good=0 mode=A last_reported="" rcount=0 since=$(date +%s) log "gestartet (DRY=$DRY)" while true; do ensure_rules sync_pve_endpoint # Pruefung fritzbox f_lan=0 if ping_via $SRC1 $LAN_PROBE; then f_lan=1; fi if [ $f_lan = 1 ] && ping_via $SRC1 $NET_PROBE; then f_probe=1; else f_probe=0; fi # Pruefung pve if ping_via $SRC2 $SRV_PROBE && ping_via $SRC2 $NET_PROBE; then p_probe=1; else p_probe=0; fi # Hysterese fritzbox if [ $f_probe = 1 ]; then f_good=$((f_good+1)); f_bad=0; else f_bad=$((f_bad+1)); f_good=0; fi if [ $f_state = up ] && [ $f_bad -ge $FAIL_N ]; then f_state=down; log "$WG1 gestoert (LAN=$f_lan)"; fi if [ $f_state = down ] && [ $f_good -ge $OK_N ]; then f_state=up; log "$WG1 wieder stabil"; fi # Hysterese pve if [ $p_probe = 1 ]; then p_good=$((p_good+1)); p_bad=0; else p_bad=$((p_bad+1)); p_good=0; fi if [ $p_state = up ] && [ $p_bad -ge $FAIL_N ]; then p_state=down; log "$WG2 gestoert"; fi if [ $p_state = down ] && [ $p_good -ge $OK_N ]; then p_state=up; log "$WG2 wieder stabil"; fi if [ $f_state = up ]; then want=A elif [ $p_state = up ]; then want=B else want=C; fi if [ "$want" != "$mode" ]; then case $want in A) log "Stufe A: Tunnel $WG1 (FRITZ!Box)";; B) log "Stufe B: Tunnel $WG2 (Server in Proxmox), $WG1 ausgefallen";; C) log "Stufe C: direkt ins Internet ohne VPN, beide Tunnel ausgefallen";; esac mode=$want; since=$(date +%s) fi apply_mode $mode $f_lan rcount=$((rcount+1)) if [ "$mode" != "$last_reported" ] || [ $rcount -ge 30 ]; then report; last_reported=$mode; rcount=0; fi { echo "stufe=$mode" echo "fritzbox=$f_state" echo "pve=$p_state" echo "fritzbox_lan=$f_lan" echo "seit=$(date -d @$since '+%d.%m.%Y %H:%M:%S' 2>/dev/null || echo $since)" echo "geprueft=$(date '+%d.%m.%Y %H:%M:%S')" echo "dry=$DRY" } > $STATE.tmp && mv $STATE.tmp $STATE sleep $INTERVAL done
Init-Skript Tunnel-Failover
Startet die Tunnel-Umschaltung beim Booten und beendet sie nicht beim Stop (damit bei einem Neustart des Dienstes keine Routen verloren gehen).
- /etc/init.d/tunnel-failover
#!/bin/sh /etc/rc.common # Tunnel-Umschaltung fritzbox -> pve -> direkt START=99 USE_PROCD=1 start_service() { procd_open_instance procd_set_param command /usr/sbin/tunnel-failover procd_set_param respawn 3600 5 0 procd_set_param stderr 1 procd_close_instance } stop_service() { : }
Manueller Uplink-Test
Misst beide Uplinks nacheinander und schreibt das Ergebnis in /tmp/uplink-test.pref; der Watchdog übernimmt es danach für 30 Minuten (TEST_HOLD). Wird vom HA-Button „Uplink testen“ über das CGI gestartet (nur von 10.10.11.12).
- /usr/sbin/uplink-test-run
#!/bin/sh # Misst beide Uplinks direkt (am Tunnel vorbei) und meldet das Ergebnis an Home Assistant Arcen. # Verbraucht ca. 2 x 1 MB ueber Landal und 2 x 1 MB ueber den Huawei. HA_HOOK=$(sed -n 's/^HA_HOOK=\(.*\)/\1/p' /usr/sbin/uplink-watchdog | head -1) URL="http://speed.cloudflare.com/__down?bytes=1000000" mess() { # $1 = Geraet; Ausgabe: mittlere kB/s und Ping ms d=$1; sum=0; n=0 for i in 1 2; do s=$(curl -s -m 20 --interface $d -o /dev/null -w '%{speed_download}' "$URL" 2>/dev/null); s=${s%%.*}; [ -z "$s" ] && s=0 sum=$((sum + s)); n=$((n + 1)) done kb=$((sum / n / 1000)) p=$(ping -c 4 -W 2 -I $d 1.1.1.1 2>/dev/null | sed -n 's/.*= [0-9.]*\/\([0-9.]*\)\/.*/\1/p') echo "$kb ${p%%.*}" } set -- $(mess phy1-sta0); L_KB=$1; L_MS=${2:-0} set -- $(mess phy0-sta0); H_KB=$1; H_MS=${2:-0} PREV=$(sed -n 's/^bevorzugt=//p' /tmp/uplink-watchdog.state 2>/dev/null | head -1) # wer deutlich (10 %) schneller ist, wird bevorzugt; sonst bleibt es wie es ist if [ "$L_KB" -ge $((H_KB * 11 / 10)) ]; then P=landal elif [ "$H_KB" -ge $((L_KB * 11 / 10)) ]; then P=huawei else P=${PREV:-landal}; fi N="Test $(date +%H:%M): Landal $((L_KB * 8 / 1000)) Mbit/s, Ping ${L_MS} ms, Huawei $((H_KB * 8 / 1000)) Mbit/s, Ping ${H_MS} ms" echo "$(date '+%d.%m.%Y %H:%M:%S') $N -> bevorzugt $P" > /tmp/uplink-test.result logger -t uplink-test "$N -> bevorzugt $P" # der uplink-watchdog uebernimmt das Ergebnis und meldet es an Home Assistant printf 'T_PREF=%s\nT_H=%s\nT_L=%s\nT_NOTE="%s"\n' "$P" "$H_KB" "$L_KB" "$N" > /tmp/uplink-test.pref
CGI Uplink-Test
Startet den Test im Hintergrund, nur von HA Arcen (10.10.11.12) erlaubt, mit Sperre gegen Doppelstart.
- /www/cgi-bin/uplink-test
#!/bin/sh echo "Content-Type: text/plain"; echo case "$REMOTE_ADDR" in 10.10.11.12) ;; *) echo "nicht erlaubt"; exit 0 ;; esac LOCK=/tmp/uplink-test.lock if [ -e $LOCK ] && [ $(( $(date +%s) - $(cat $LOCK) )) -lt 150 ]; then echo "laeuft schon"; exit 0; fi date +%s > $LOCK ( /usr/sbin/uplink-test-run >/dev/null 2>&1; rm -f $LOCK ) >/dev/null 2>&1 & echo "Test gestartet"
Anpassungen nach Update (anpassungen-anwenden)
Idempotent, kann beliebig oft laufen. Setzt: Landal ohne VHT (hostapd.sh, supplicant.uc), multicast_to_unicast Standard 0. Nach einem Update einmal ausführen, dann wifi down radio1; sleep 3; wifi up radio1. (wifi reload reicht nicht.)
- /usr/sbin/anpassungen-anwenden
#!/bin/sh # Eigene Anpassungen (09.10.2026) nach einem Firmware-Update erneut anwenden. Idempotent. # Pfade lassen sich zum Testen ueberschreiben: H=... W=... anpassungen-anwenden H="${H:-/lib/netifd/hostapd.sh}" W="${W:-/lib/netifd/netifd-wireless.sh}" MARK="Eigene Anpassung 09.10.2026" OK=0 # 1) Landal-Client: VHT (und damit HE) abschalten if [ -f "$H" ] && ! grep -q "$MARK" "$H"; then awk -v mark="$MARK" ' { print } /^wpa_supplicant_add_network\(\)/ { f = 1 } f && !d && /\[ -n "\$ocv" \] && append network_data "ocv=\$ocv"/ { print "" print "\t# " mark ": Die Landal-Zugangspunkte kuendigen HE an, antworten aber nur mit VHT;" print "\t# der Treiber bricht dann ab. Ohne VHT (und damit ohne HE) verbindet sich der Client per HT." print "\t[ \"$_w_mode\" = \"sta\" ] && [ \"$ssid\" = \"Free WiFi Landal\" ] && append network_data \"disable_vht=1\" \"$N$T\"" d = 1 }' "$H" > "$H.neu" && grep -q "$MARK" "$H.neu" && sh -n "$H.neu" && { cp "$H" "$H.orig-vor-anpassung"; cat "$H.neu" > "$H"; OK=$((OK+1)); } rm -f "$H.neu" fi # 2) WLAN-Geraete am selben AP: multicast_to_unicast Vorgabe 0 if [ -f "$W" ] && ! grep -q "$MARK" "$W"; then sed 's|^\(\t\)\(\[ \${multicast_to_unicast:-\)1\(} -gt 0 -o \${proxy_arp:-0} -gt 0 \] \&\& json_add_boolean isolate 1\)|\1# '"$MARK"': Vorgabe 0 statt 1, damit sich WLAN-Geraete am selben Zugangspunkt erreichen\n\1\20\3|' "$W" > "$W.neu" \ && grep -q "$MARK" "$W.neu" && sh -n "$W.neu" && { cp "$W" "$W.orig-vor-anpassung"; cat "$W.neu" > "$W"; OK=$((OK+1)); } rm -f "$W.neu" fi # 3) OpenWrt 25.12: Landal-Client ohne VHT (die Client-Konfiguration entsteht in supplicant.uc) U="${U:-/usr/share/ucode/wifi/supplicant.uc}" if [ -f "$U" ] && ! grep -q "$MARK\|Eigene Anpassung 10.10.2026" "$U"; then awk '{ print } /^\tconfig\.scan_ssid = 1;$/ && !d { print "" print "\t/* Eigene Anpassung 10.10.2026: Die Landal-Zugangspunkte kuendigen HE an, antworten aber nur mit VHT." print "\t * Ohne VHT (und damit ohne HE) verbindet sich der Client per HT und laeuft stabil. */" print "\tif (config.mode == '"'"'sta'"'"' && config.ssid == '"'"'Free WiFi Landal'"'"')" print "\t\tconfig.disable_vht = true;" d = 1 }' "$U" > "$U.neu" && grep -q "Eigene Anpassung 10.10.2026" "$U.neu" && { cp "$U" "$U.orig-vor-anpassung"; cat "$U.neu" > "$U"; OK=$((OK+1)); NEEDS_WIFI=1; } rm -f "$U.neu" fi logger -t anpassungen "angewendet: $OK Anpassung(en)" [ "$NEEDS_WIFI" = 1 ] && echo "WLAN-Client neu starten: wifi down radio1; sleep 3; wifi up radio1" echo "angewendet: $OK"
Landal: Änderung in supplicant.uc
Unter OpenWrt 25.12 wird die WLAN-Client-Konfiguration in /usr/share/ucode/wifi/supplicant.uc erzeugt, nicht mehr per Shell. Der Landal-Zugangspunkt kündigt HE an, antwortet aber nur mit VHT, dann bricht die Verbindung ab. Der Patch schaltet VHT (und damit HE) für diese SSID aus, der Client läuft per HT. Eingefügt in setup_sta nach config.scan_ssid = 1;. Das Original liegt als /root/backup-20261010/supplicant.uc.orig.
- supplicant.uc (Ausschnitt)
/* Eigene Anpassung 10.10.2026: Die Landal-Zugangspunkte kuendigen HE an, antworten aber nur mit VHT. * Ohne VHT (und damit ohne HE) verbindet sich der Client per HT und laeuft stabil. */ if (config.mode == 'sta' && config.ssid == 'Free WiFi Landal') config.disable_vht = true;
Landal HT/VHT-Vergleich (landal-vergleich)
Siehe auch landal_wlan. Ablauf: Sperrdatei, Prüfung (Landal verbunden? Huawei online? nicht schon auf Landal erzwungen?), Uplink auf Huawei stellen, je Modus mit wpa_cli set_network disable_vht + reassociate neu verbinden, 2 × 1 MB Download, 10 Pings, Sendefehlerzähler; danach alles zurückstellen und eine Zeile an die CSV anhängen. Umgebungsvariablen: LV_QUELLE (cron|ha), LV_ALARM (scharf|huelle|unscharf).
- /usr/sbin/landal-vergleich
#!/bin/sh # Vergleicht den Landal-WLAN-Client in HT (disable_vht=1) und VHT (disable_vht=0). # Laeuft lokal auf dem Router: per Cron (07:45, 12:45, 19:45) und stuendlich von Home Assistant, # wenn die Alarmanlage scharf ist (CGI /www/cgi-bin/landal-vergleich). Unabhaengig vom Mac. # # Ablauf: # 1. Tunnel vorab auf den Huawei legen (Vorgabe "huawei" in /tmp/uplink-mode, wartet bis der # uplink-watchdog umgeschaltet hat). Dadurch ist der Tunnel waehrend der Messung nicht gestoert. # 2. Landal-Client in HT bzw. VHT stellen, je Modus 2 x 1 MB Download (direkt ueber phy1-sta0) # und 10 Pings, dazu Sendefehlerzaehler. # 3. Grundzustand wiederherstellen, Vorgabe freigeben, Daten speichern und an HA melden. # Daten: /root/landal-vergleich.csv (eine Zeile je Lauf mit Kopfzeile), Auswertung: landal-auswertung # Braucht wpa_cli und curl. D=phy1-sta0 WPA="wpa_cli -p /var/run/wpa_supplicant -i $D" CSV=/root/landal-vergleich.csv LOCK=/tmp/landal-vergleich.lock CONF=/var/run/wpa-supplicant-phy1-sta0.conf HOOKFILE=/etc/landal-hook MODEFILE=/tmp/uplink-mode STATE=/tmp/uplink-watchdog.state URL="http://speed.cloudflare.com/__down?bytes=1000000" KEEP7=168 # Statistik "7 Tage" = letzte 168 Laeufe (bei stuendlicher Messung) SRC="${LV_QUELLE:-cron}" # cron oder ha ALARM="${LV_ALARM:-}" # von HA: scharf, huelle, unscharf HEADER="epoch;datum;zeit;wochentag;stunde;tageszeit;quelle;alarm;reihenfolge;bssid;freq_mhz;signal_dbm;uplink_vorher;ht_mbit10;ht_dl1_kbs;ht_dl2_kbs;ht_fehler_pm;ht_ping_avg;ht_ping_max;ht_pakete;ht_fehler;ht_rate_mbit;vht_mbit10;vht_dl1_kbs;vht_dl2_kbs;vht_fehler_pm;vht_ping_avg;vht_ping_max;vht_pakete;vht_fehler;vht_rate_mbit;ht_pingverlust_pct;vht_pingverlust_pct" log() { logger -t landal-vergleich "$*"; echo "$(date +%H:%M:%S) $*"; } if [ -e $LOCK ] && [ $(( $(date +%s) - $(cat $LOCK) )) -lt 900 ]; then exit 0; fi command -v wpa_cli >/dev/null && command -v curl >/dev/null || { log "wpa_cli oder curl fehlt"; exit 1; } iw dev $D link | grep -q Connected || { log "Landal nicht verbunden, Messung uebersprungen"; exit 0; } NID=$($WPA list_networks | awk '/Landal/{print $1; exit}') [ -n "$NID" ] || { log "Landal-Netzwerk nicht gefunden"; exit 1; } BASE=0; grep -q "disable_vht=1" $CONF 2>/dev/null && BASE=1 # 1 = HT erzwungen, 0 = VHT erlaubt # Vorgabe merken. Steht sie von Hand auf Landal, wird nicht gemessen. PREV_SET=0; PREV="" if [ -r $MODEFILE ]; then PREV=$(cat $MODEFILE); PREV_SET=1; fi [ "$PREV" = landal ] && { log "Vorgabe steht von Hand auf Landal, Messung uebersprungen"; exit 0; } [ "$(sed -n 's/^huawei_internet=//p' $STATE 2>/dev/null)" = 1 ] || { log "Huawei ohne Internet, Messung uebersprungen"; exit 0; } date +%s > $LOCK cleanup() { $WPA set_network $NID disable_vht $BASE >/dev/null 2>&1; $WPA reassociate >/dev/null 2>&1 if [ $PREV_SET = 1 ]; then echo "$PREV" > $MODEFILE; else rm -f $MODEFILE; fi rm -f $LOCK } trap cleanup EXIT # 1. Tunnel auf den Huawei legen UPL_VORHER=$(sed -n 's/^uplink=//p' $STATE 2>/dev/null) echo huawei > $MODEFILE cur="" for i in 1 2 3 4 5 6 7 8 9 10 11 12; do sleep 5 cur=$(sed -n 's/^uplink=//p' $STATE 2>/dev/null) [ "$cur" = Huawei ] && break done [ "$cur" = Huawei ] || { log "Tunnel ist nicht auf den Huawei gewechselt, Messung abgebrochen"; exit 0; } sleep 8 # Tunnel auf dem Huawei kurz einschwingen lassen stat() { iw dev $D station dump | awk '/tx packets:/{p=$3} /tx failed:/{f=$3} END{print p+0, f+0}'; } set_mode() { # $1 = 1 (HT) oder 0 (VHT); wartet, bis die Neuverbindung wirklich steht $WPA set_network $NID disable_vht $1 >/dev/null $WPA reassociate >/dev/null sleep 2 for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do ct=$(iw dev $D station dump 2>/dev/null | awk '/connected time/{print $3; exit}') if [ -n "$ct" ] && [ "$ct" -le 8 ] && ip -4 addr show $D | grep -q inet; then break; fi sleep 2 done sleep 6 } messen() { # Ausgabe: mbit10 dl1 dl2 fehler_promille ping_avg ping_max pakete fehler rate ping_verlust_prozent ; Fehler -1 = ungueltig set -- $(stat); p0=$1; f0=$2 ct0=$(iw dev $D station dump 2>/dev/null | awk '/connected time/{print $3; exit}'); ct0=${ct0:-0} sum=0; n=0; dl1=0; dl2=0 for i in 1 2; do s=$(curl -s -m 25 --interface $D -o /dev/null -w '%{speed_download}' "$URL" 2>/dev/null); s=${s%%.*} [ -z "$s" ] && s=0 [ $i = 1 ] && dl1=$((s / 1000)) || dl2=$((s / 1000)) sum=$((sum + s)); n=$((n + 1)) done m10=$(( sum / n * 8 / 100000 )) PO=$(ping -c 10 -W 2 -I $D 1.1.1.1 2>/dev/null) P=$(echo "$PO" | sed -n 's/.*= \([0-9.]*\)\/\([0-9.]*\)\/\([0-9.]*\).*/\2 \3/p') ploss=$(echo "$PO" | sed -n 's/.* \([0-9]*\)% packet loss.*/\1/p'); ploss=${ploss:-100} set -- $P; pavg=${1%%.*}; pmax=${2%%.*}; pavg=${pavg:-0}; pmax=${pmax:-0} rate=$(iw dev $D link | sed -n 's/.*tx bitrate: \([0-9.]*\) MBit.*/\1/p'); rate=${rate:-0} set -- $(stat); p1=$1; f1=$2 ct1=$(iw dev $D station dump 2>/dev/null | awk '/connected time/{print $3; exit}'); ct1=${ct1:-0} dp=$((p1 - p0)); df=$((f1 - f0)) if [ $dp -lt 0 ] || [ $df -lt 0 ] || [ $ct1 -lt $ct0 ]; then echo "0 0 0 -1 0 0 0 0 0 0"; return; fi tot=$((dp + df)); [ $tot -lt 1 ] && tot=1 echo "$m10 $dl1 $dl2 $(( df * 1000 / tot )) $pavg $pmax $tot $df $rate $ploss" } messen_gueltig() { r=$(messen) case "$r" in "0 0 0 -1 "*) sleep 10; r=$(messen);; esac echo "$r" } # Kopfzeile / altes Format wegsichern if [ -f $CSV ] && [ "$(head -n 1 $CSV | cut -d';' -f1)" != "epoch" ]; then mv $CSV $CSV.alt; fi [ -f $CSV ] || echo "$HEADER" > $CSV NL=$(grep -c -v '^epoch;' $CSV) if [ $(( NL % 2 )) = 0 ]; then FIRST=1; SECOND=0; ORD="HT-VHT"; else FIRST=0; SECOND=1; ORD="VHT-HT"; fi log "Start ($SRC, Alarm=${ALARM:--}, Reihenfolge $ORD, Grundzustand $([ $BASE = 1 ] && echo HT || echo VHT))" BSSID=$(iw dev $D link | sed -n 's/^Connected to \([0-9a-f:]*\).*/\1/p') FREQ=$(iw dev $D link | sed -n 's/.*freq: \([0-9]*\).*/\1/p') SIG=$(iw dev $D link | sed -n 's/.*signal: \(-[0-9]*\).*/\1/p') set_mode $FIRST; R1=$(messen_gueltig) set_mode $SECOND; R2=$(messen_gueltig) if [ $FIRST = 1 ]; then HT="$R1"; VHT="$R2"; else HT="$R2"; VHT="$R1"; fi set -- $HT; HM=$1; HD1=$2; HD2=$3; HF=$4; HPA=$5; HPM=$6; HPK=$7; HFL=$8; HR=$9; HLO=${10} set -- $VHT; VM=$1; VD1=$2; VD2=$3; VF=$4; VPA=$5; VPM=$6; VPK=$7; VFL=$8; VR=$9; VLO=${10} if [ "$HF" -lt 0 ] || [ "$VF" -lt 0 ]; then log "Messung ungueltig (Verbindung baute sich waehrend der Messung neu auf), nicht gespeichert"; exit 0; fi H=$(date +%H); H=${H#0}; H=${H:-0} if [ "$H" -lt 6 ]; then B=nachts; elif [ "$H" -lt 12 ]; then B=morgens; elif [ "$H" -lt 18 ]; then B=mittags; else B=abends; fi WT=$(date +%a) echo "$(date +%s);$(date +%d.%m.%Y);$(date +%H:%M);$WT;$H;$B;$SRC;${ALARM:--};$ORD;$BSSID;$FREQ;$SIG;${UPL_VORHER:--};$HM;$HD1;$HD2;$HF;$HPA;$HPM;$HPK;$HFL;$HR;$VM;$VD1;$VD2;$VF;$VPA;$VPM;$VPK;$VFL;$VR;$HLO;$VLO" >> $CSV # Datei begrenzen (ca. 1,5 Jahre bei stuendlicher Messung) [ "$(wc -l < $CSV)" -gt 15000 ] && { cp $CSV $CSV.$(date +%Y%m%d); { echo "$HEADER"; tail -n 8000 $CSV; } > $CSV.neu && mv $CSV.neu $CSV; } stat_text() { # $1 = Spalte Mbit10, $2 = Fehler-Promille, $3 = Ping avg ; $4 = Anzahl letzte Laeufe (0 = alle) if [ "$4" = 0 ]; then grep -v '^epoch;' $CSV; else grep -v '^epoch;' $CSV | tail -n $4; fi | awk -F';' -v m=$1 -v f=$2 -v p=$3 ' { n++; sm+=$m; sf+=$f; sp+=$p; b=$6; bn[b]++; bs[b]+=$m } END { if (n == 0) { print "keine Daten"; exit } printf "Mittel %.1f Mbit/s, Fehler %.1f Prozent, Ping %.0f ms, %d Laeufe. Nach Tageszeit: nachts %.1f, morgens %.1f, mittags %.1f, abends %.1f Mbit/s", sm/n/10, sf/n/10, sp/n, n, (bn["nachts"] ? bs["nachts"]/bn["nachts"]/10 : 0), (bn["morgens"] ? bs["morgens"]/bn["morgens"]/10 : 0), (bn["mittags"] ? bs["mittags"]/bn["mittags"]/10 : 0), (bn["abends"] ? bs["abends"]/bn["abends"]/10 : 0) }' | sed 's/\./,/g' } ST_HT=$(stat_text 14 17 18 $KEEP7) ST_VHT=$(stat_text 23 26 27 $KEEP7) ST_ALLE="Seit Beginn. HT: $(stat_text 14 17 18 0 | cut -c1-110) | VHT: $(stat_text 23 26 27 0 | cut -c1-110)" ST_ALLE=$(echo "$ST_ALLE" | cut -c1-250) num() { awk -v v="$1" -v d="$2" 'BEGIN{printf "%.1f", v/d}'; } INFO="Messung $(date +%d.%m.%H:%M) ($B, $SRC$([ -n "$ALARM" ] && echo ", Alarm $ALARM")): HT $(num $HM 10) Mbit/s (Fehler $(num $HF 10) Prozent, Ping $HPA ms, Verlust $HLO Prozent), VHT $(num $VM 10) Mbit/s (Fehler $(num $VF 10) Prozent, Ping $VPA ms, Verlust $VLO Prozent)" log "$INFO" if [ -f $HOOKFILE ]; then HOOK=$(cat $HOOKFILE) DATA="ht_mbit=$(num $HM 10)&vht_mbit=$(num $VM 10)&ht_fehler=$(num $HF 10)&vht_fehler=$(num $VF 10)&ht_ping=$HPA&vht_ping=$VPA" DATA="$DATA&info=$(echo "$INFO" | tr ' ' '+')&stat_ht=$(echo "$ST_HT" | tr ' ' '+')&stat_vht=$(echo "$ST_VHT" | tr ' ' '+')&stat_alle=$(echo "$ST_ALLE" | tr ' ' '+')" wget -q -T 10 -O /dev/null --post-data="$DATA" "$HOOK" 2>/dev/null && log "an Home Assistant gemeldet" || log "Meldung an Home Assistant fehlgeschlagen" fi
CGI Landal-Vergleich
Wird stündlich von HA Arcen aufgerufen, wenn die Alarmanlage scharf ist (Automation „Landal-Vergleich stündlich bei scharfer Anlage“, Aufruf über downloader.download_file).
- /www/cgi-bin/landal-vergleich
#!/bin/sh # Startet den Landal-Vergleich (HT gegen VHT). Aufruf nur von Home Assistant Arcen (10.10.11.12). # Parameter: ?alarm=scharf|huelle|unscharf echo "Content-Type: text/plain"; echo case "$REMOTE_ADDR" in 10.10.11.12) ;; *) echo "nicht erlaubt"; exit 0 ;; esac A=$(echo "$QUERY_STRING" | sed -n 's/.*alarm=\([a-z]*\).*/\1/p') ( LV_QUELLE=ha LV_ALARM="$A" /usr/sbin/landal-vergleich >/dev/null 2>&1 & ) echo "gestartet"
Auswertung (landal-auswertung)
Aufruf: landal-auswertung [Tage]. Ausgabe: Gesamt, nach Tageszeit, Alarmstatus, Stunde und je Tag; Mbit/s Durchschnitt, Sendefehler in %, Ping und Anteil der Läufe, in denen VHT schneller war.
- /usr/sbin/landal-auswertung
#!/bin/sh # Auswertung von /root/landal-vergleich.csv (HT gegen VHT am Landal-Client). Aufruf: landal-auswertung [Tage] # Ohne Angabe: alle Daten. Spalten siehe Kopfzeile der CSV (Mbit/s sind dort mal 10, Fehler in Promille). CSV=/root/landal-vergleich.csv DAYS="${1:-0}" [ -f $CSV ] || { echo "keine Daten: $CSV"; exit 1; } SINCE=0; [ "$DAYS" -gt 0 ] && SINCE=$(( $(date +%s) - DAYS * 86400 )) grep -v '^epoch;' $CSV | awk -F';' -v since=$SINCE ' function pr(name, n, hm, vm, hf, vf, hp, vp, vw) { if (n + 0 == 0) return printf "%-22s n=%3d | HT %5.1f Mbit/s F %4.1f%% Ping %4.0f | VHT %5.1f Mbit/s F %4.1f%% Ping %4.0f | VHT schneller in %2.0f%%\n", name, n, hm/n/10, hf/n/10, hp/n, vm/n/10, vf/n/10, vp/n, vw*100/n } $1 >= since { n++; hm+=$14; vm+=$23; hf+=$17; vf+=$26; hp+=$18; vp+=$27; vw+=($23>$14) b=$6; bn[b]++; bhm[b]+=$14; bvm[b]+=$23; bhf[b]+=$17; bvf[b]+=$26; bhp[b]+=$18; bvp[b]+=$27; bvw[b]+=($23>$14) a=$8; an[a]++; ahm[a]+=$14; avm[a]+=$23; ahf[a]+=$17; avf[a]+=$26; ahp[a]+=$18; avp[a]+=$27; avw[a]+=($23>$14) h=$5+0; hn[h]++; hhm[h]+=$14; hvm[h]+=$23; hhf[h]+=$17; hvf[h]+=$26; hhp[h]+=$18; hvp[h]+=$27; hvw[h]+=($23>$14) d=$2; dn[d]++; dhm[d]+=$14; dvm[d]+=$23; dhf[d]+=$17; dvf[d]+=$26; dhp[d]+=$18; dvp[d]+=$27; dvw[d]+=($23>$14) if (!(d in dord)) { dord[d]=++nd; dname[nd]=d } } END { print "=== Gesamt ==="; pr("alle Laeufe", n, hm, vm, hf, vf, hp, vp, vw) print "\n=== nach Tageszeit ===" split("nachts morgens mittags abends", bl, " "); for (i = 1; i <= 4; i++) { b = bl[i]; pr(b, bn[b], bhm[b], bvm[b], bhf[b], bvf[b], bhp[b], bvp[b], bvw[b]) } print "\n=== nach Alarmstatus (scharf/huelle/unscharf, - = Cron) ===" for (a in an) pr(a, an[a], ahm[a], avm[a], ahf[a], avf[a], ahp[a], avp[a], avw[a]) print "\n=== nach Stunde ===" for (h = 0; h < 24; h++) pr(sprintf("%02d Uhr", h), hn[h], hhm[h], hvm[h], hhf[h], hvf[h], hhp[h], hvp[h], hvw[h]) print "\n=== je Tag ===" for (i = 1; i <= nd; i++) { d = dname[i]; pr(d, dn[d], dhm[d], dvm[d], dhf[d], dvf[d], dhp[d], dvp[d], dvw[d]) } }'
Cron (/etc/crontabs/root)
Täglicher Neustart um 04:30 und die drei Messungen (07:45, 12:45, 19:45). Zusätzlich stündliche Läufe durch HA bei scharfer Anlage.
- /etc/crontabs/root
30 4 * * * sleep 70 && touch /etc/banner && reboot 45 7 * * * /usr/sbin/landal-vergleich >/dev/null 2>&1 45 12 * * * /usr/sbin/landal-vergleich >/dev/null 2>&1 45 19 * * * /usr/sbin/landal-vergleich >/dev/null 2>&1
Hinweise
- Benötigte Pakete:
wpa-cli(für den Vergleich),curl. - SSH-Zugang: Schlüssel
~/.ssh/openwrt_claude(Mac). Nach dem Einspielen von Archiven vom Macchown -R root:root, sonst lehnt Dropbearauthorized_keysab.
