#!/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