5 files changed
+154
-4
No files matched your search
@@ -23,6 +23,7 @@
|
|||||||
@include border-radius;
|
@include border-radius;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
.net-add-btn { margin-right: $popup-scale * 2pt; }
|
||||||
|
|
||||||
// Bluetooth device list
|
// Bluetooth device list
|
||||||
.bt-device-row { margin-bottom: $popup-scale * 4pt; }
|
.bt-device-row { margin-bottom: $popup-scale * 4pt; }
|
||||||
|
|||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pick a nearby WiFi network in wofi and connect to it through wpa_supplicant
|
||||||
|
# (userControlled: the control socket is open to wheel). Networks added here
|
||||||
|
# live in wpa_supplicant's memory only — the config file is a sops secret and
|
||||||
|
# is never rewritten — so they are gone after a reboot.
|
||||||
|
detach_self "$@"
|
||||||
|
|
||||||
|
eww close popup 2>/dev/null
|
||||||
|
eww update active-panel= 2>/dev/null
|
||||||
|
|
||||||
|
iface=""
|
||||||
|
for s in /run/wpa_supplicant/*; do
|
||||||
|
[[ -S $s && ${s##*/} != p2p-* ]] && { iface=${s##*/}; break; }
|
||||||
|
done
|
||||||
|
|
||||||
|
notify() { notify-send -a WiFi -i network-wireless "$@"; }
|
||||||
|
[[ -n $iface ]] || { notify "WiFi" "wpa_supplicant is not running"; exit 1; }
|
||||||
|
|
||||||
|
wpa() { wpa_cli -g "/run/wpa_supplicant/$iface" "$@"; }
|
||||||
|
field() { awk -F= -v k="$1" '$1 == k { print substr($0, length(k) + 2); exit }'; }
|
||||||
|
|
||||||
|
signal_icon() {
|
||||||
|
if (($1 >= -50)); then echo ""
|
||||||
|
elif (($1 >= -60)); then echo ""
|
||||||
|
elif (($1 >= -70)); then echo ""
|
||||||
|
elif (($1 >= -80)); then echo ""
|
||||||
|
else echo ""; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# wpa_cli prints SSIDs with printf-style escapes (\xNN, \\, \")
|
||||||
|
unescape() { printf '%b' "$1"; }
|
||||||
|
|
||||||
|
saved_id() { # SSID → network id
|
||||||
|
local id s
|
||||||
|
while IFS=$'\t' read -r id s _; do
|
||||||
|
[[ $(unescape "$s") == "$1" ]] && { echo "$id"; return; }
|
||||||
|
done < <(wpa list_networks | tail -n +2)
|
||||||
|
}
|
||||||
|
|
||||||
|
wpa scan >/dev/null
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
current=$(unescape "$(wpa status | field ssid)")
|
||||||
|
|
||||||
|
declare -A ssid_of flags_of
|
||||||
|
lines=()
|
||||||
|
# strongest BSS per SSID, strongest first; hidden networks are skipped
|
||||||
|
while IFS=$'\t' read -r sig flags raw; do
|
||||||
|
ssid=$(unescape "$raw")
|
||||||
|
[[ -n $ssid && $ssid != *$'\n'* ]] || continue
|
||||||
|
line="$(signal_icon "$sig") $ssid"
|
||||||
|
[[ $flags == *PSK* || $flags == *SAE* || $flags == *EAP* ]] && line+=" "
|
||||||
|
[[ $ssid == "$current" ]] && line+=" "
|
||||||
|
ssid_of[$line]=$ssid
|
||||||
|
flags_of[$line]=$flags
|
||||||
|
lines+=("$line")
|
||||||
|
done < <(wpa scan_results | awk -F'\t' '
|
||||||
|
NR > 1 && $5 != "" { if (!($5 in best) || $3 > best[$5]) { best[$5] = $3; flags[$5] = $4 } }
|
||||||
|
END { for (s in best) printf "%s\t%s\t%s\n", best[s], flags[s], s }' | sort -t$'\t' -k1,1nr)
|
||||||
|
|
||||||
|
((${#lines[@]})) || { notify "WiFi" "No networks found"; exit 0; }
|
||||||
|
|
||||||
|
choice=$(printf '%s\n' "${lines[@]}" | wofi --dmenu -p "WiFi")
|
||||||
|
[[ -n $choice && -n ${ssid_of[$choice]+x} ]] || exit 0
|
||||||
|
ssid=${ssid_of[$choice]}
|
||||||
|
flags=${flags_of[$choice]}
|
||||||
|
[[ $ssid == "$current" ]] && exit 0
|
||||||
|
|
||||||
|
id=$(saved_id "$ssid")
|
||||||
|
new=0
|
||||||
|
if [[ -z $id ]]; then
|
||||||
|
[[ $flags == *EAP* ]] && { notify "$ssid" "Enterprise (EAP) networks are not supported"; exit 1; }
|
||||||
|
id=$(wpa add_network | tail -n 1)
|
||||||
|
[[ $id =~ ^[0-9]+$ ]] || { notify "$ssid" "Could not add the network"; exit 1; }
|
||||||
|
new=1
|
||||||
|
wpa set_network "$id" ssid "$(printf '%s' "$ssid" | od -An -tx1 | tr -d ' \n')" >/dev/null
|
||||||
|
if [[ $flags == *PSK* || $flags == *SAE* ]]; then
|
||||||
|
psk=$(wofi --dmenu --password -p "Password for $ssid" </dev/null)
|
||||||
|
if ((${#psk} < 8 || ${#psk} > 63)); then
|
||||||
|
wpa remove_network "$id" >/dev/null
|
||||||
|
[[ -n $psk ]] && notify "$ssid" "The password must be 8 to 63 characters"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
wpa set_network "$id" psk "\"$psk\"" >/dev/null
|
||||||
|
if [[ $flags == *SAE* ]]; then
|
||||||
|
wpa set_network "$id" key_mgmt WPA-PSK SAE >/dev/null
|
||||||
|
wpa set_network "$id" ieee80211w 1 >/dev/null
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
wpa set_network "$id" key_mgmt NONE >/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# select_network disables every other network; re-enable them afterwards so
|
||||||
|
# the saved ones still auto-connect later.
|
||||||
|
wpa select_network "$id" >/dev/null
|
||||||
|
for ((i = 0; i < 30; i++)); do
|
||||||
|
sleep 0.5
|
||||||
|
st=$(wpa status)
|
||||||
|
if [[ $(field wpa_state <<<"$st") == COMPLETED && $(field id <<<"$st") == "$id" ]]; then
|
||||||
|
wpa enable_network all >/dev/null
|
||||||
|
notify "Connected" "$ssid"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
((new)) && wpa remove_network "$id" >/dev/null
|
||||||
|
wpa enable_network all >/dev/null
|
||||||
|
wpa reassociate >/dev/null
|
||||||
|
notify "Could not connect to $ssid" "$( ((new)) && echo "Wrong password?" || echo "Timed out")"
|
||||||
|
exit 1
|
||||||
@@ -15,8 +15,9 @@ api="https://www.radiorecord.ru/api"
|
|||||||
unit=eww-radio
|
unit=eww-radio
|
||||||
state_dir=${XDG_STATE_HOME:-$HOME/.local/state}/eww
|
state_dir=${XDG_STATE_HOME:-$HOME/.local/state}/eww
|
||||||
cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}/eww
|
cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}/eww
|
||||||
|
img_dir=$cache_dir/radio-img
|
||||||
id_file=$state_dir/radio-station
|
id_file=$state_dir/radio-station
|
||||||
mkdir -p "$state_dir" "$cache_dir"
|
mkdir -p "$state_dir" "$cache_dir" "$img_dir"
|
||||||
|
|
||||||
station_ids='[507,522,523,536,537,42532,42602]'
|
station_ids='[507,522,523,536,537,42532,42602]'
|
||||||
# Extra stations: {id (unique integer >= 1000000), title, stream_hls, icon_fill_white ("" for none)}
|
# Extra stations: {id (unique integer >= 1000000), title, stream_hls, icon_fill_white ("" for none)}
|
||||||
@@ -47,6 +48,32 @@ stations() {
|
|||||||
printf '%s\n' "$out"
|
printf '%s\n' "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# GTK loads CSS url() images synchronously on the UI thread: a remote URL
|
||||||
|
# freezes the whole daemon until the download finishes (or times out). eww
|
||||||
|
# only ever gets file:// URIs of cached copies.
|
||||||
|
local_img() { # URL → file:// URI, "" if unavailable
|
||||||
|
local url=$1 f
|
||||||
|
case $url in
|
||||||
|
file://*) printf '%s' "$url"; return ;;
|
||||||
|
/*) printf 'file://%s' "$url"; return ;;
|
||||||
|
http://* | https://*) ;;
|
||||||
|
*) return ;;
|
||||||
|
esac
|
||||||
|
f=$img_dir/$(cksum <<<"$url" | cut -d' ' -f1)
|
||||||
|
if [[ ! -s $f ]]; then
|
||||||
|
curl -sfL --max-time 5 -o "$f.tmp" "$url" 2>/dev/null && mv "$f.tmp" "$f" || { rm -f "$f.tmp"; return; }
|
||||||
|
fi
|
||||||
|
printf 'file://%s' "$f"
|
||||||
|
}
|
||||||
|
|
||||||
|
localize_stations() {
|
||||||
|
local url map='{}'
|
||||||
|
while IFS= read -r url; do
|
||||||
|
map=$(jq -c --arg k "$url" --arg v "$(local_img "$url")" '.[$k] = $v' <<<"$map")
|
||||||
|
done < <(jq -r '.[].icon_fill_white // empty' <<<"$STATIONS" | sort -u)
|
||||||
|
STATIONS=$(jq -c --argjson m "$map" 'map(.icon_fill_white = ($m[.icon_fill_white // ""] // ""))' <<<"$STATIONS")
|
||||||
|
}
|
||||||
|
|
||||||
station() { # ID FIELD
|
station() { # ID FIELD
|
||||||
jq -r --argjson id "${1:-0}" --arg f "$2" 'first(.[] | select(.id == $id) | .[$f]) // empty' <<<"$STATIONS"
|
jq -r --argjson id "${1:-0}" --arg f "$2" 'first(.[] | select(.id == $id) | .[$f]) // empty' <<<"$STATIONS"
|
||||||
}
|
}
|
||||||
@@ -141,7 +168,8 @@ song_playing() { # ID → SONG from the station history (radiorecord) or the tit
|
|||||||
if [[ $(station "$id" radiorecord) == true ]]; then
|
if [[ $(station "$id" radiorecord) == true ]]; then
|
||||||
fetched=$(curl -sf --compressed --max-time 8 "$api/station/history/?id=$id" 2>/dev/null \
|
fetched=$(curl -sf --compressed --max-time 8 "$api/station/history/?id=$id" 2>/dev/null \
|
||||||
| jq -c '.result.history[0] | {artist: (.artist // ""), song: (.song // ""), image600: (.image600 // "")}' 2>/dev/null)
|
| jq -c '.result.history[0] | {artist: (.artist // ""), song: (.song // ""), image600: (.image600 // "")}' 2>/dev/null)
|
||||||
[[ -n $fetched && $fetched != null ]] && SONG=$fetched
|
[[ -n $fetched && $fetched != null ]] \
|
||||||
|
&& SONG=$(jq -c --arg i "$(local_img "$(jq -r .image600 <<<"$fetched")")" '.image600 = $i' <<<"$fetched")
|
||||||
else
|
else
|
||||||
SONG=$(jq -cn --arg t "$(station "$id" title)" '{artist: "", song: $t, image600: ""}')
|
SONG=$(jq -cn --arg t "$(station "$id" title)" '{artist: "", song: $t, image600: ""}')
|
||||||
fi
|
fi
|
||||||
@@ -159,7 +187,7 @@ media_event() {
|
|||||||
if [[ -z $player ]]; then
|
if [[ -z $player ]]; then
|
||||||
MEDIA=$default_media MEDIA_PLAYER=""
|
MEDIA=$default_media MEDIA_PLAYER=""
|
||||||
elif [[ $player != mpv* ]]; then # the radio and jellyfin are mpv, not "external"
|
elif [[ $player != mpv* ]]; then # the radio and jellyfin are mpv, not "external"
|
||||||
MEDIA=$(media_json "$player" "$status" "$artist" "$title" "$art")
|
MEDIA=$(media_json "$player" "$status" "$artist" "$title" "$(local_img "$art")")
|
||||||
MEDIA_PLAYER=$player
|
MEDIA_PLAYER=$player
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -206,7 +234,9 @@ refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
status_loop() {
|
status_loop() {
|
||||||
|
find "$img_dir" -type f -mtime +7 -delete 2>/dev/null
|
||||||
STATIONS=$(stations)
|
STATIONS=$(stations)
|
||||||
|
localize_stations
|
||||||
SONG=$default_song MEDIA=$default_media MEDIA_PLAYER="" PLAYING="" LAST_SONG=0 LAST_VOL=""
|
SONG=$default_song MEDIA=$default_media MEDIA_PLAYER="" PLAYING="" LAST_SONG=0 LAST_VOL=""
|
||||||
refresh
|
refresh
|
||||||
mux_init
|
mux_init
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
(box :orientation "h" :space-evenly false :valign "center" :class "sys-section-header"
|
(box :orientation "h" :space-evenly false :valign "center" :class "sys-section-header"
|
||||||
(box :class "section-accent ${accent}")
|
(box :class "section-accent ${accent}")
|
||||||
(label :class "sys-label" :text title :hexpand true :halign "start")
|
(label :class "sys-label" :text title :hexpand true :halign "start")
|
||||||
|
(children)
|
||||||
(button
|
(button
|
||||||
:class "net-toggle-btn ${enabled ? 'net-toggle-on' : 'net-toggle-off'}"
|
:class "net-toggle-btn ${enabled ? 'net-toggle-on' : 'net-toggle-off'}"
|
||||||
:onclick onclick
|
:onclick onclick
|
||||||
@@ -23,7 +24,13 @@
|
|||||||
:title "WiFi" :accent "wifi-accent"
|
:title "WiFi" :accent "wifi-accent"
|
||||||
:enabled {net.wifi.enabled}
|
:enabled {net.wifi.enabled}
|
||||||
:onclick {net.wifi.enabled ? "rfkill block wlan" : "rfkill unblock wlan"}
|
:onclick {net.wifi.enabled ? "rfkill block wlan" : "rfkill unblock wlan"}
|
||||||
:icon-on "" :icon-off "")
|
:icon-on "" :icon-off ""
|
||||||
|
(button
|
||||||
|
:class "net-toggle-btn net-toggle-on net-add-btn"
|
||||||
|
:visible {net.wifi.enabled}
|
||||||
|
:onclick "scripts/net/wifi-connect"
|
||||||
|
:tooltip "Connect to a network"
|
||||||
|
(label :text "")))
|
||||||
(box :orientation "v" :space-evenly false
|
(box :orientation "v" :space-evenly false
|
||||||
:visible {net.wifi.connected}
|
:visible {net.wifi.connected}
|
||||||
(netinfo-row :label "SSID" :value {net.wifi.ssid})
|
(netinfo-row :label "SSID" :value {net.wifi.ssid})
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ let
|
|||||||
"scripts/net/bt" = mkScript "bt" ./bar/scripts/net/bt [ jq glib ];
|
"scripts/net/bt" = mkScript "bt" ./bar/scripts/net/bt [ jq glib ];
|
||||||
"scripts/net/bt-toggle" = mkScript "bt-toggle" ./bar/scripts/net/bt-toggle [ bluez ];
|
"scripts/net/bt-toggle" = mkScript "bt-toggle" ./bar/scripts/net/bt-toggle [ bluez ];
|
||||||
"scripts/net/wg-toggle" = mkScript "wg-toggle" ./bar/scripts/net/wg-toggle [ ];
|
"scripts/net/wg-toggle" = mkScript "wg-toggle" ./bar/scripts/net/wg-toggle [ ];
|
||||||
|
"scripts/net/wifi-connect" = mkScript "wifi-connect" ./bar/scripts/net/wifi-connect [ eww wpa_supplicant wofi libnotify ];
|
||||||
"scripts/sys/gpu" = mkScript "gpu" ./bar/scripts/sys/gpu [ custom.amdgpu_top jq ];
|
"scripts/sys/gpu" = mkScript "gpu" ./bar/scripts/sys/gpu [ custom.amdgpu_top jq ];
|
||||||
"scripts/sys/memory" = mkScript "memory" ./bar/scripts/sys/memory [ ];
|
"scripts/sys/memory" = mkScript "memory" ./bar/scripts/sys/memory [ ];
|
||||||
"scripts/sys/battery" = mkScript "battery" ./bar/scripts/sys/battery [ ];
|
"scripts/sys/battery" = mkScript "battery" ./bar/scripts/sys/battery [ ];
|
||||||
|
|||||||
Reference in new issue
Block a user