#!/usr/bin/env bash # Default sink / source volume for the popup. Refreshes on PipeWire events # (pactl subscribe on the pipewire-pulse socket) and every 30 s. # # volume status loop (deflisten) # volume setvol SINK|SOURCE PERCENT # volume mute SINK|SOURCE icons=("󰕿" "󰖀" "󰕾") level() { # prints " " wpctl get-volume "@DEFAULT_AUDIO_$1@" 2>/dev/null \ | awk '{ printf "%d %s\n", $2 * 100 + 0.5, ($0 ~ /MUTED/) ? "true" : "false" }' } sink_name() { wpctl inspect @DEFAULT_AUDIO_SINK@ 2>/dev/null \ | awk -F'"' '/node\.nick/ { n = $2 } /node\.description/ { d = $2 } END { print (n != "" ? n : d) }' } state() { local vol muted mic mic_muted icon mic_icon lvl read -r vol muted < <(level SINK) read -r mic mic_muted < <(level SOURCE) vol=${vol:-0} muted=${muted:-false} mic=${mic:-0} mic_muted=${mic_muted:-false} lvl=$((vol >= 67 ? 2 : vol >= 34 ? 1 : 0)) if [[ $muted == true ]]; then icon="󰝟"; else icon=${icons[$lvl]}; fi if [[ $mic_muted == true ]]; then mic_icon="󰍭"; else mic_icon="󰍬"; fi printf '{"icon":"%s","percent":%d,"sink_muted":%s,"mic_icon":"%s","microphone":%d,"source_muted":%s,"sink":"%s"}' \ "$icon" "$vol" "$muted" "$mic_icon" "$mic" "$mic_muted" "$(jstr "$(sink_name)")" } refresh() { emit "$(state)"; } _pw_events() { pactl subscribe 2>/dev/null | grep --line-buffered -E "on (sink|source|server) "; } case ${1:-} in mute) wpctl set-mute "@DEFAULT_AUDIO_$2@" toggle ;; setvol) wpctl set-volume -l 1.0 "@DEFAULT_AUDIO_$2@" "${3%.*}%" ;; *) refresh mux_init mux_add _pw_events mux_tick 30 mux_loop refresh ;; esac