wren-linux / bin / radios
#!/bin/bash
# radios: written by Wren Linux parts/wren-menu.sh
# Switch Wi-Fi, Bluetooth, the microphone and (with extras/webcam.sh) a USB
# webcam on and off, for the Wren menu
# and the command line. Only what this computer has is listed.
# radios list what there is and whether it is on
# radios NAME [status|on|off|toggle] NAME is wifi, bluetooth or mic
# radios mic off --wait waits for the microphone to appear first
# (used at login, when sound is still starting)
# Wi-Fi and Bluetooth go through rfkill (the kernel's radio switch, which
# NetworkManager and bluez both respect), so "off" is the radio itself being
# off, not just a program being told not to use it. The microphone is muted in
# PipeWire: that is software, and a program that runs as you can unmute it
# again; a hardware switch or a disabled device is the stronger kind.
# Exit status: 0 done, 1 not available on this computer (or the microphone
# didn't switch), 2 bad use.
export PATH="$PATH:/usr/sbin:/sbin"
SYS=${WREN_SYS:-/sys}
SOURCE=@DEFAULT_AUDIO_SOURCE@
# the webcam switch (extras/webcam.sh), run as root through sudo for on and off
CAMERA=${WREN_CAMERA:-/usr/local/libexec/wren-camera}
usage() { echo "usage: radios list | radios wifi|bluetooth|mic|camera [status|on|off|toggle]" >&2; exit 2; }
exists() {
case $1 in
wifi) command -v rfkill >/dev/null && { compgen -G "$SYS/class/net/*/wireless" || compgen -G "$SYS/class/ieee80211/*"; } >/dev/null ;;
# Bluetooth only with bluez (extras/bluetooth.sh): without it the switch
# would only free the radio, and say "on" with nothing working
bluetooth) command -v rfkill >/dev/null && command -v bluetoothctl >/dev/null && compgen -G "$SYS/class/bluetooth/*" >/dev/null ;;
# a source, really: with no sound hardware PipeWire has a Dummy Output (a sink) and no
# microphone, and wpctl then answers for the default microphone with that sink's volume
mic) command -v wpctl >/dev/null && wpctl get-volume "$SOURCE" >/dev/null 2>&1 &&
wpctl inspect "$SOURCE" 2>/dev/null | grep -q 'media\.class = "Audio/Source' ;;
camera) [ -x "$CAMERA" ] && [ "$("$CAMERA" status 2>/dev/null)" != none ] ;;
*) return 1 ;;
esac
}
# state NAME: on, off, or blocked (a hardware switch holds it off)
state() {
local out
case $1 in
wifi | bluetooth)
out=$(rfkill -n -o SOFT,HARD list "$1" 2>/dev/null)
if grep -q ' blocked' <<<"$out"; then echo blocked
elif grep -q '^blocked' <<<"$out"; then echo off
elif [ "$1" = bluetooth ] && command -v bluetoothctl >/dev/null \
&& ! bluetoothctl show 2>/dev/null | grep -q 'Powered: yes'; then
# the radio is free but the adapter is powered down (bluez starts it off)
echo off
else echo on; fi ;;
mic)
if wpctl get-volume "$SOURCE" 2>/dev/null | grep -q MUTED; then echo off; else echo on; fi ;;
camera) "$CAMERA" status ;;
esac
}
set_state() { # NAME on|off
local rc=0
case $1 in
wifi) if [ "$2" = on ]; then rfkill unblock wifi; else rfkill block wifi; fi ;;
bluetooth)
if [ "$2" = on ]; then
rfkill unblock bluetooth
if command -v bluetoothctl >/dev/null; then
# right after unblocking, bluez can take a moment to see the adapter
bluetoothctl power on >/dev/null 2>&1 || { sleep 1; bluetoothctl power on >/dev/null 2>&1; }
fi
else
command -v bluetoothctl >/dev/null && bluetoothctl power off >/dev/null 2>&1
rfkill block bluetooth
fi ;;
mic)
# Checked afterwards: a mute that didn't happen must not pass for one (the
# bar would say "mic ON" while this said off)
wpctl set-mute "$SOURCE" "$([ "$2" = on ] && echo 0 || echo 1)" || true
if [ "$(state mic)" != "$2" ]; then
echo "radios: the microphone didn't switch $2 (PipeWire refused, or it isn't ready yet: wpctl status)." >&2
rc=1
fi ;;
camera)
# On asks your password, so a program running as you can't switch it on. In the
# desktop, in a window of its own: this may have been started from the menu or a key.
if [ "$2" = off ]; then sudo -n "$CAMERA" off >/dev/null
elif [ -n "${WAYLAND_DISPLAY:-}" ] && command -v foot >/dev/null; then
foot --app-id=wren-run --title="Webcam on" sudo -p "Your password switches the webcam on: " "$CAMERA" on
else sudo -p "Your password switches the webcam on: " "$CAMERA" on >/dev/null
fi ;;
esac
# the bar shows the Wi-Fi name: have it look again (only if that pid really is sway-status)
local pid
pid=$(cat "${XDG_RUNTIME_DIR:-/tmp}/sway-status.pid" 2>/dev/null)
if [ -n "$pid" ] && { tr '\0' ' ' <"/proc/$pid/cmdline"; } 2>/dev/null | grep -q sway-status; then
kill -USR1 "$pid" 2>/dev/null
fi
return "$rc"
}
cmd=${1:-}
case $cmd in
list)
for r in wifi bluetooth mic camera; do
if exists "$r"; then echo "$r $(state "$r")"; fi
done ;;
wifi | bluetooth | mic | camera)
action=${2:-status}
if [ "${3:-}" = --wait ] || [ "$action" = --wait ] || [ "${4:-}" = --wait ]; then
for _ in $(seq 20); do exists "$cmd" && break; sleep 1; done
[ "$action" != --wait ] || action=status
fi
if [ "$cmd" = bluetooth ] && ! command -v bluetoothctl >/dev/null && compgen -G "$SYS/class/bluetooth/*" >/dev/null; then
echo "radios: Bluetooth isn't installed. Install it: wrenctl install bluetooth" >&2
exit 1
fi
exists "$cmd" || { echo "radios: no $cmd on this computer." >&2; exit 1; }
case $action in
status) state "$cmd" ;;
on | off) set_state "$cmd" "$action" ;;
toggle) if [ "$(state "$cmd")" = on ]; then set_state "$cmd" off; else set_state "$cmd" on; fi ;;
*) usage ;;
esac ;;
*) usage ;;
esac