sync: auto-sync from HOWARD-HOME at 2026-06-15 20:40:48
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-15 20:40:48
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# live-stats.sh — Plane-2 live RF/airtime from the UOS Network API (classic session API).
|
||||
# Gives CURRENT per-AP per-radio cu_total / cu_self / num_sta / satisfaction / tx_retries and the
|
||||
# AP RF-neighbor table — for before/after validation of changes and (the neighbor table) the
|
||||
# materials-aware AP-to-AP coverage graph that unlocks confident radio DISABLES.
|
||||
# Gives CURRENT per-AP per-radio cu_total / cu_self / num_sta / retry% and device-level
|
||||
# satisfaction, plus the worst-clients view (signal / retry% / satisfaction_reason) — for
|
||||
# before/after validation of changes.
|
||||
# NOTE: satisfaction is populated at the DEVICE level (per-radio is -1 on this controller),
|
||||
# and tx_retries is a cumulative counter so we report radio_table_stats.tx_retries_pct (a rate).
|
||||
# TODO: AP-to-AP RF-neighbor table (for confident radio DISABLES) — not a single API field;
|
||||
# build it from the `rogue` collection by matching our own APs' vap_table BSSIDs. Not done yet.
|
||||
#
|
||||
# AUTH (provision once): the classic API needs a controller admin session. Create a dedicated
|
||||
# READ-ONLY admin in the UniFi UI (OS Settings -> Admins -> add a Viewer), then vault it:
|
||||
@@ -52,21 +56,23 @@ echo "[INFO] site short=$SHORT"
|
||||
|
||||
curl -sk -b "$CJ" "$base/proxy/network/api/s/$SHORT/stat/device" | python -c "
|
||||
import sys,json
|
||||
for d in json.load(sys.stdin).get('data',[]):
|
||||
if d.get('type')!='uap': continue
|
||||
print('AP',d.get('name'),'clients=',d.get('num_sta'))
|
||||
aps=[d for d in json.load(sys.stdin).get('data',[]) if d.get('type')=='uap']
|
||||
print('# APs reporting:',len(aps))
|
||||
for d in sorted(aps,key=lambda a:str(a.get('name'))):
|
||||
# device-level satisfaction is the populated one (per-radio satisfaction is -1 on this controller)
|
||||
print('AP',d.get('name'),'clients=',d.get('num_sta'),'satisfaction=',d.get('satisfaction'))
|
||||
for r in d.get('radio_table_stats',[]):
|
||||
print(' ',r.get('radio'),'ch',r.get('channel'),'cu_total',r.get('cu_total'),'cu_self_rx',r.get('cu_self_rx'),'cu_self_tx',r.get('cu_self_tx'),'num_sta',r.get('num_sta'),'tx_retries',r.get('tx_retries'),'satisfaction',r.get('satisfaction'))
|
||||
# RF neighbor table (materials-aware AP-to-AP visibility) if present
|
||||
for n in (d.get('radio_table') or []):
|
||||
pass
|
||||
" 2>&1 | head -60
|
||||
print(' ',r.get('radio'),'ch',r.get('channel'),'cu_total',r.get('cu_total'),'cu_self_rx',r.get('cu_self_rx'),'cu_self_tx',r.get('cu_self_tx'),'num_sta',r.get('num_sta'),'retry%',r.get('tx_retries_pct'))
|
||||
" 2>&1
|
||||
|
||||
if [ "$WANT_CLIENTS" = "--clients" ]; then
|
||||
echo "=== clients (rssi/rate/retries) ==="
|
||||
echo "=== worst wireless clients by satisfaction (signal / retry% / why) ==="
|
||||
curl -sk -b "$CJ" "$base/proxy/network/api/s/$SHORT/stat/sta" | python -c "
|
||||
import sys,json
|
||||
for c in json.load(sys.stdin).get('data',[])[:40]:
|
||||
print(' ',c.get('hostname') or c.get('mac'),'ap',c.get('ap_mac'),'rssi',c.get('rssi'),'signal',c.get('signal'),'tx_rate',c.get('tx_rate'),'retries',c.get('tx_retries'),'sat',c.get('satisfaction'))
|
||||
" 2>&1 | head -45
|
||||
cs=[c for c in json.load(sys.stdin).get('data',[]) if not c.get('is_wired')]
|
||||
cs.sort(key=lambda c:(c.get('satisfaction') if isinstance(c.get('satisfaction'),(int,float)) else 999))
|
||||
print('# wireless clients:',len(cs),' (worst 40 by satisfaction)')
|
||||
for c in cs[:40]:
|
||||
print(' ',(c.get('hostname') or c.get('mac')),'sat',c.get('satisfaction'),'signal',c.get('signal'),'noise',c.get('noise'),'retry%',c.get('wifi_tx_retries_percentage'),'band',c.get('radio'),'ch',c.get('channel'),'why',c.get('satisfaction_reason'))
|
||||
" 2>&1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user