#!/usr/bin/env bash # rice-net - network state for the eww panel, and the actions it runs. # # rice-net list JSON: Wi-Fi radio, active connection, networks, VPNs # rice-net radio on|off|toggle # rice-net connect id from the last list; asks for a password in rofi if needed # rice-net disconnect # rice-net rescan # rice-net vpn bring a VPN / WireGuard connection up or down # rice-net share show the current Wi-Fi network as a QR code # # SSIDs are chosen by whoever broadcasts them, so the panel only ever passes # the numeric id; the SSID is looked up again here from the cached list. set -uo pipefail run="${XDG_RUNTIME_DIR:-/tmp}/rice" cache="$run/wifi.tsv" vpncache="$run/vpn.tsv" mkdir -p "$run" say() { rice-notify send "$1" "${2:-}" -i network-wireless; } poke() { date +%s%N > "$run/net.rev"; } signal_glyph() { local s=$1 if (( s >= 75 )); then echo 󰤨 elif (( s >= 50 )); then echo 󰤥 elif (( s >= 25 )); then echo 󰤢 else echo 󰤟; fi } list() { local radio=false has_wifi=false active_type="" active_name="" active_dev="" nmcli -g TYPE device 2>/dev/null | grep -qx wifi && has_wifi=true [[ $has_wifi == true && $(nmcli -t radio wifi 2>/dev/null) == enabled ]] && radio=true IFS=: read -r active_type active_name active_dev < <(nmcli -g TYPE,STATE,CONNECTION,DEVICE device status 2>/dev/null | awk -F: '$2 == "connected" && ($1 == "wifi" || $1 == "ethernet") { print $1 ":" $3 ":" $4; exit }') local known known=$(nmcli -g NAME,TYPE connection show 2>/dev/null | awk -F: '$2 == "802-11-wireless" { print $1 }') # IN-USE:SIGNAL:SECURITY:SSID, strongest first, one row per SSID, top 8. : > "$cache" local inuse signal security ssid id=0 seen=$'\n' while IFS=: read -r inuse signal security ssid; do ssid=${ssid//\\:/:} [[ -z $ssid || $seen == *$'\n'"$ssid"$'\n'* ]] && continue seen+="$ssid"$'\n' printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$id" "$ssid" "$signal" "${security:---}" \ "$([[ $inuse == '*' ]] && echo 1 || echo 0)" \ "$(grep -Fxq -- "$ssid" <<<"$known" && echo 1 || echo 0)" >> "$cache" (( ++id >= 8 )) && break done < <(nmcli -g IN-USE,SIGNAL,SECURITY,SSID device wifi list --rescan no 2>/dev/null | sort -t: -k2,2nr) # VPN and WireGuard profiles known to NetworkManager. : > "$vpncache" local active_names name type vid=0 active_names=$(nmcli -g NAME connection show --active 2>/dev/null) while IFS=: read -r name type; do [[ $type == vpn || $type == wireguard ]] || continue printf '%s\t%s\t%s\n' "$vid" "$name" "$(grep -Fxq -- "$name" <<<"$active_names" && echo 1 || echo 0)" >> "$vpncache" (( vid++ )) done < <(nmcli -g NAME,TYPE connection show 2>/dev/null) jq -cn --argjson radio "$radio" --argjson haswifi "$has_wifi" \ --arg atype "$active_type" --arg aname "$active_name" \ --argjson wifi "$(while IFS=$'\t' read -r i s sig sec use kn; do jq -cn --argjson id "$i" --arg ssid "$s" --argjson signal "$sig" --arg sec "$sec" \ --argjson active "$([[ $use == 1 ]] && echo true || echo false)" \ --argjson known "$([[ $kn == 1 ]] && echo true || echo false)" \ --arg glyph "$(signal_glyph "$sig")" \ '{id: $id, ssid: $ssid, signal: $signal, active: $active, known: $known, glyph: $glyph, sub: ([(if $active then "connected" elif $known then "saved" else empty end), (if $sec == "--" then "open" else "󰌾 " + $sec end)] | join(" · "))}' done < "$cache" | jq -cs '.')" \ --argjson vpn "$(while IFS=$'\t' read -r i n a; do jq -cn --argjson id "$i" --arg name "$n" --argjson active "$([[ $a == 1 ]] && echo true || echo false)" \ '{id: $id, name: $name, active: $active}' done < "$vpncache" | jq -cs '.')" \ '{radio: $radio, active: {type: $atype, name: $aname}, hasWifi: $haswifi, wifi: $wifi, vpn: $vpn}' } field() { awk -F'\t' -v id="$1" -v col="$2" '$1 == id { print $col; exit }' "$3"; } connect() { [[ ${1:-} =~ ^[0-9]+$ ]] || { echo "rice-net: connect takes an id" >&2; exit 2; } local ssid security known pass ssid=$(field "$1" 2 "$cache"); security=$(field "$1" 4 "$cache"); known=$(field "$1" 6 "$cache") [[ -n $ssid ]] || { say "Wi-Fi" "That network is gone; rescan and try again"; exit 1; } if [[ $known == 1 ]]; then nmcli connection up id "$ssid" >/dev/null 2>&1 && say "Connected" "$ssid" || say "Could not connect" "$ssid" elif [[ $security == "--" ]]; then nmcli device wifi connect "$ssid" >/dev/null 2>&1 && say "Connected" "$ssid" || say "Could not connect" "$ssid" elif [[ $security == *802.1X* ]]; then say "Enterprise network" "Opening the connection editor for $ssid" setsid -f nm-connection-editor >/dev/null 2>&1 else pass=$(rofi -dmenu -password -theme "$HOME/.config/rofi/prompt.rasi" -p "$ssid" -mesg "Wi-Fi password") || exit 0 [[ -n $pass ]] || exit 0 local mgmt=wpa-psk [[ $security == *WPA3* && $security != *WPA2* ]] && mgmt=sae # The password reaches nmcli through a file descriptor, not the command line. nmcli connection add type wifi con-name "$ssid" ssid "$ssid" wifi-sec.key-mgmt "$mgmt" >/dev/null 2>&1 if nmcli connection up id "$ssid" passwd-file <(printf '802-11-wireless-security.psk:%s\n' "$pass") >/dev/null 2>&1; then say "Connected" "$ssid" else nmcli connection delete id "$ssid" >/dev/null 2>&1 say "Could not connect" "Wrong password for $ssid?" fi fi poke } case "${1:-}" in list) list ;; radio) case "${2:-toggle}" in on) nmcli radio wifi on ;; off) nmcli radio wifi off ;; *) [[ $(nmcli -t radio wifi) == enabled ]] && nmcli radio wifi off || nmcli radio wifi on ;; esac poke ;; connect) connect "${2:-}" ;; disconnect) dev=$(nmcli -g TYPE,DEVICE,STATE device status | awk -F: '$1 == "wifi" && $3 == "connected" { print $2; exit }') [[ -n $dev ]] && nmcli device disconnect "$dev" >/dev/null && say "Wi-Fi" "Disconnected" poke ;; rescan) nmcli device wifi rescan >/dev/null 2>&1; poke ;; vpn) [[ ${2:-} =~ ^[0-9]+$ ]] || { echo "rice-net: vpn takes an id" >&2; exit 2; } name=$(field "$2" 2 "$vpncache"); active=$(field "$2" 3 "$vpncache") [[ -n $name ]] || exit 1 if [[ $active == 1 ]]; then nmcli connection down id "$name" >/dev/null && say "VPN off" "$name" else nmcli connection up id "$name" >/dev/null && say "VPN on" "$name" || say "VPN failed" "$name"; fi poke ;; share) name=$(nmcli -g TYPE,STATE,CONNECTION device status | awk -F: '$1 == "wifi" && $2 == "connected" { print $3; exit }') [[ -n $name ]] || { say "Wi-Fi" "Not connected to a Wi-Fi network"; exit 1; } ssid=$(nmcli -g 802-11-wireless.ssid connection show id "$name") psk=$(nmcli -s -g 802-11-wireless-security.psk connection show id "$name") esc() { sed -e 's/[\\;,:"]/\\&/g' <<<"$1"; } png="$run/wifi-qr.png" printf 'WIFI:T:%s;S:%s;P:%s;;' "$([[ -n $psk ]] && echo WPA || echo nopass)" "$(esc "$ssid")" "$(esc "$psk")" | qrencode -o "$png" -s 10 -m 2 --foreground="$(rice-theme get bg | tr -d '#')" --background="$(rice-theme get fg | tr -d '#')" setsid -f feh --title "Wi-Fi: $ssid" --geometry 420x420 -Z "$png" >/dev/null 2>&1 ;; *) sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;; esac