Skip to content

Instantly share code, notes, and snippets.

@lg
Last active April 24, 2026 15:37
Show Gist options
  • Select an option

  • Save lg/e91d1c5c9640d963e13dbb1901bb4396 to your computer and use it in GitHub Desktop.

Select an option

Save lg/e91d1c5c9640d963e13dbb1901bb4396 to your computer and use it in GitHub Desktop.
auto disconnects clients with a low signal strength on LEDE / OpenWRT
#!/bin/ash
#
# angry_wifi.sh
#
# auto disconnects clients with a low signal strength on LEDE / OpenWRT. great
# for clients who hold onto a lower-signal-strength accesspoint instead of
# roaming. before running, make sure to download the latest MAC addresses with:
#
# wget --no-check-certificate -O - 'https://services13.ieee.org/RST/standards-ra-web/rest/assignments/download/?registry=MAC&text=apple' | cut -d, -f2 > apple_mac_addresses
#
# some notes:
# - only Apple clients will be disconnected
# - clients won't be re-disconnected for 1 minute if they end up re-connecting
# - be careful with this script, if you don't have solid wifi everywhere clients
# will constantly be disconnected
IFS=$'\n'
while true; do
for LINE in $(iwinfo wlan0 assoclist | grep SNR); do
MAC=$(echo "$LINE" | awk '{ print $1 }')
SIGNAL=$(echo "$LINE" | awk '{ print $2 }')
MAC_PREFIX=$(echo "$MAC" | sed -e 's/://g' | cut -c1-6)
if [ "$SIGNAL" -lt "-80" ]; then
if find . | grep "angry_wifi_client_$MAC" > /dev/null; then
date ; logger -s -t angry_wifi "Low signal client $MAC ($SIGNAL) is back, not disconnecting yet."
# Only allow Apple devices to roam
elif grep "$MAC_PREFIX" apple_mac_addresses; then
date ; logger -s -t angry_wifi "Low signal client $MAC ($SIGNAL) being disconnected."
ubus call hostapd.wlan0 del_client "{'addr':'$MAC', 'reason':5, 'deauth':false, 'ban_time':0}"
# Add to do-not-disconnect list
touch "angry_wifi_client_${MAC}_$(date +%s)"
fi
fi
done
# Remove from the do-not-disconnect list
CURDATE=$(date +%s)
for FILE in $(find . | grep angry_wifi_client); do
TIME=$(echo "$FILE" | cut -d'_' -f 5)
TIME_SINCE=$((CURDATE - TIME))
MAC=$(echo "$FILE" | cut -d'_' -f 4)
if [ "$TIME_SINCE" -gt "60" ]; then
date ; logger -s -t angry_wifi "Low signal client $MAC removed from do-not-disconnect."
rm "$FILE"
fi
done
sleep 1
done
@lifeeth
Copy link
Copy Markdown

lifeeth commented Oct 22, 2016

Thanks for sharing this. Was looking to hack up something similar and google pointed me to you :)

@benjamin-army
Copy link
Copy Markdown

only Apple clients will be disconnected

Why is that?

Thanks

@barbieri
Copy link
Copy Markdown

I needed something more robust, so I got concepts from this gist and https://github.com/mk248/lede-modhostapd and created a pure-Lua script that uses /etc/config/wireless, taking signal OR signal-noise-ratio, supports whitelists (like Apple devices), blacklists and is a bit more efficient, not touching the filesystem to persist state.

https://github.com/barbieri/barbieri-playground/tree/master/openwrt/wifi-disconnect-low-signal

however I really hope OpenWRT/LEDE will integrate the modhostapd patch, then we can avoid an extra process in the system.

@mxcxpx
Copy link
Copy Markdown

mxcxpx commented Apr 24, 2026

SMART ROAMING – KOMPLETTE REBUILD-ANLEITUNG (OpenWrt 25 + APK + LuCI)
🧹 1. ALLES ALTEN KRAM LÖSCHEN (CLEAN RESET)
rm -f /root/smart_roam.sh
rm -rf /usr/share/ucode/luci/controller/smartroam.uc
rm -rf /www/luci-static/resources/view/smartroam.js
🧱 2. UCI KONFIG ERSTELLEN
cat > /etc/config/smart_roam << 'EOF'
config smart_roam 'cfg1'
option threshold_24 '-80'
option threshold_5 '-83'
option cooldown '60'
EOF
🔧 3. SMART ROAM SCRIPT (WLAN KICK LOGIK)
cat > /root/smart_roam.sh << 'EOF'
#!/bin/sh

CONFIG_24=$(uci get smart_roam.cfg1.threshold_24 2>/dev/null)
CONFIG_5=$(uci get smart_roam.cfg1.threshold_5 2>/dev/null)
COOLDOWN=$(uci get smart_roam.cfg1.cooldown 2>/dev/null)

THRESHOLD_24="${CONFIG_24:- -80}"
THRESHOLD_5="${CONFIG_5:- -83}"
COOLDOWN="${COOLDOWN:-60}"
INTERVAL=5

STATE_FILE="/tmp/smart_roam_state"
touch "$STATE_FILE"

get_threshold() {
echo "$1" | grep -q "phy1-ap0" && echo "$THRESHOLD_5" || echo "$THRESHOLD_24"
}

while true; do
for IFACE in phy0-ap0 phy1-ap0; do
iwinfo "$IFACE" assoclist 2>/dev/null | while read -r line; do

        MAC=$(echo "$line" | awk '/[0-9A-Fa-f:]{17}/ {print $1}')
        SIGNAL=$(echo "$line" | grep -oE '-[0-9]+' | head -n1)

        [ -z "$MAC" ] && continue
        [ -z "$SIGNAL" ] && continue

        NOW=$(date +%s)
        LAST=$(grep "$MAC" "$STATE_FILE" | awk '{print $2}')
        [ -z "$LAST" ] && LAST=0

        DIFF=$((NOW - LAST))
        THRESHOLD=$(get_threshold "$IFACE")

        if [ "$SIGNAL" -lt "$THRESHOLD" ] && [ "$DIFF" -gt "$COOLDOWN" ]; then
            ubus call hostapd."$IFACE" del_client "{\"addr\":\"$MAC\",\"reason\":5,\"deauth\":true}"
            sed -i "/$MAC/d" "$STATE_FILE"
            echo "$MAC $NOW" >> "$STATE_FILE"
        fi

    done
done

sleep "$INTERVAL"

done
EOF
⚙️ 4. SCRIPT STARTBAR MACHEN
chmod +x /root/smart_roam.sh
🚀 5. UCI TEST (OPTIONAL CHECK)
uci show smart_roam
🌐 6. LUCI SMART ROAM PANEL (SLIDER UI)
mkdir -p /www/luci-static/resources/view
cat > /www/luci-static/resources/view/smartroam.js << 'EOF'
'use strict';
'require view';
'require form';
'require uci';

return view.extend({

load: function() {
    return uci.load('smart_roam');
},

render: function() {

    var m = new form.Map('smart_roam', 'Smart Roaming');

    var s = m.section(form.NamedSection, 'cfg1', 'smart_roam');

    var o1 = s.option(form.Value, 'threshold_24', '2.4 GHz RSSI');
    o1.datatype = 'range(-100,-30)';

    var o2 = s.option(form.Value, 'threshold_5', '5 GHz RSSI');
    o2.datatype = 'range(-100,-30)';

    var o3 = s.option(form.Value, 'cooldown', 'Cooldown Sekunden');

    return m.render();
}

});
EOF
🔄 7. LUCI + WEB SERVER NEU STARTEN
/etc/init.d/rpcd restart
/etc/init.d/uhttpd restart
🌐 8. WEB UI ÖFFNEN
http://ip....../cgi-bin/luci/admin/network/smartroam
⚡ ERGEBNIS

Du hast danach:

✔ Smart Roaming Script aktiv
✔ Web Slider für RSSI
✔ Cooldown steuerbar
✔ keine Terminal Arbeit nötig
✔ sofort wirksame Änderungen

🧠 WENN DU ES NOCH ROBUSTER WILLST

Dann kann ich dir noch bauen:

🔥
Auto-Start Service (Boot persistent)
🔥
Live Client Dashboard
🔥
„Sticky Client Protection“
🔥
echtes Band Steering (kein Kick mehr)

Sag einfach:

👉 pro panel

Dann wird dein Router ein kleiner Enterprise WiFi Controller 👍

@mxcxpx
Copy link
Copy Markdown

mxcxpx commented Apr 24, 2026

i did that with chat gpt and after 2h hard work with this fuckin ki it fianlly work

@mxcxpx
Copy link
Copy Markdown

mxcxpx commented Apr 24, 2026

OpenWrt Smart Roaming – Schritt-für-Schritt
Anleitung
Diese Anleitung erklärt Schritt für Schritt die Installation eines Smart Roaming Systems auf OpenWrt (WR3000H).
Alle Befehle werden im SSH-Terminal ausgeführt. Nach jedem Befehl ENTER drücken.

im Terminal:
-> ssh root@ IP router

  1. ALTE DATEIEN ENTFERNEN - beim ersten mal kann man das weg lassen, und falls was schief geht hier anfangen

Befehl:
rm -f /root/smart_roam.sh
rm -rf /www/luci-static/resources/view/smartroam.js
ENTER drücken nach jeder Zeile.

  1. UCI KONFIG ERSTELLEN

cat > /etc/config/smart_roam << 'EOF'
config smart_roam 'cfg1'
option threshold_24 '-80'
option threshold_5 '-83'
option cooldown '60'
EOF
ENTER nach EOF.

  1. SMART ROAM SCRIPT ERSTELLEN

cat > /root/smart_roam.sh << 'EOF'
#!/bin/sh
CONFIG_24=$(uci get smart_roam.cfg1.threshold_24 2>/dev/null)
CONFIG_5=$(uci get smart_roam.cfg1.threshold_5 2>/dev/null)
COOLDOWN=$(uci get smart_roam.cfg1.cooldown 2>/dev/null)
THRESHOLD_24="${CONFIG_24:- -80}"
THRESHOLD_5="${CONFIG_5:- -83}"
COOLDOWN="${COOLDOWN:-60}"
INTERVAL=5
STATE_FILE="/tmp/smart_roam_state"
touch "$STATE_FILE"
get_threshold() {
echo "$1" | grep -q "phy1-ap0" && echo "$THRESHOLD_5" || echo "$THRESHOLD_24"
}
while true; do
for IFACE in phy0-ap0 phy1-ap0; do
iwinfo "$IFACE" assoclist 2>/dev/null | while read -r line; do
MAC=$(echo "$line" | awk '/[0-9A-Fa-f:]{17}/ {print $1}')
SIGNAL=$(echo "$line" | grep -oE '-[0-9]+' | head -n1)
[ -z "$MAC" ] && continue
[ -z "$SIGNAL" ] && continue
NOW=$(date +%s)
LAST=$(grep "$MAC" "$STATE_FILE" | awk '{print $2}')
[ -z "$LAST" ] && LAST=0
DIFF=$((NOW - LAST))
THRESHOLD=$(get_threshold "$IFACE")
if [ "$SIGNAL" -lt "$THRESHOLD" ] && [ "$DIFF" -gt "$COOLDOWN" ]; then
ubus call hostapd."$IFACE" del_client "{"addr":"$MAC","reason":5,"deauth":true}"
sed -i "/$MAC/d" "$STATE_FILE"
echo "$MAC $NOW" >> "$STATE_FILE"
fi
done
done
sleep "$INTERVAL"
done
EOF

  1. SCRIPT STARTBAR MACHEN

chmod +x /root/smart_roam.sh

  1. STARTEN

/root/smart_roam.sh &

  1. WEB INTERFACE (LUCI)

Im Browser öffnen:
http://192.168.0.1/cgi-bin/luci/admin/network/smartroam

  1. WICHTIG

  • Änderungen im Web wirken sofort
  • Keine Neustarts nötig
  • Werte steuern WLAN Verhalten automatisch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment