#!/usr/bin/env bash # Volume / mic control with a dunst OSD. usage: volume up|down|mute|micmute|set set -uo pipefail STEP="${VOLUME_STEP:-5}" MAX="${VOLUME_MAX:-100}" SINK="@DEFAULT_AUDIO_SINK@" SOURCE="@DEFAULT_AUDIO_SOURCE@" ID=2593 # fixed notification id => the OSD replaces itself instead of stacking have() { command -v "$1" >/dev/null 2>&1; } have wpctl || { echo "wpctl (wireplumber) not found" >&2; exit 1; } get_volume() { wpctl get-volume "$SINK" | awk '{printf "%d", $2 * 100}' } is_muted() { wpctl get-volume "$SINK" | grep -q MUTED } osd() { have dunstify || return 0 local icon="$1" text="$2" value="$3" dunstify -a osd -u low -r "$ID" -h "int:value:$value" -i "$icon" "$text" } case "${1:-}" in up) wpctl set-mute "$SINK" 0 wpctl set-volume -l "$(awk "BEGIN{print $MAX/100}")" "$SINK" "${STEP}%+" ;; down) wpctl set-mute "$SINK" 0 wpctl set-volume "$SINK" "${STEP}%-" ;; set) wpctl set-volume -l "$(awk "BEGIN{print $MAX/100}")" "$SINK" "${2:?need a percentage}%" ;; mute) wpctl set-mute "$SINK" toggle ;; micmute) wpctl set-mute "$SOURCE" toggle if wpctl get-volume "$SOURCE" | grep -q MUTED; then osd microphone-sensitivity-muted "Microphone muted" 0 else osd microphone-sensitivity-high "Microphone live" 100 fi exit 0 ;; ""|-h|--help) echo "usage: volume up|down|mute|micmute|set "; exit 0 ;; *) echo "unknown action: $1" >&2; exit 2 ;; esac vol="$(get_volume)" if is_muted; then osd audio-volume-muted "Muted" 0 elif (( vol >= 66 )); then osd audio-volume-high "Volume ${vol}%" "$vol" elif (( vol >= 33 )); then osd audio-volume-medium "Volume ${vol}%" "$vol" else osd audio-volume-low "Volume ${vol}%" "$vol" fi # Nudge polybar so the bar updates instantly rather than on its 5s poll pkill -RTMIN+1 polybar 2>/dev/null || true