#!/usr/bin/env zsh # Minimal PipeWire sink cycler using starred lines from wpctl status. # Deps: pw-dump, jq, wpctl set -euo pipefail # Collect sink IDs (Audio/Sink nodes) sinks=($(pw-dump | jq -r '.[] | select(.info.props."media.class"=="Audio/Sink") | .id')) (( ${#sinks} )) || exit 0 # Get all lines containing a star (default indicators for various classes) starred=$(wpctl status | grep -F '*') || true # Extract numbers (IDs) from starred lines (the number before the first dot after the star) typeset -a star_ids while IFS= read -r line; do # Match: * . if [[ $line =~ [*][[:space:]]*([0-9]+)\. ]]; then star_ids+=("${match[1]}") fi done <<< "$starred" # Find first starred ID that is actually one of our sinks current="" for cid in "${star_ids[@]}"; do for s in "${sinks[@]}"; do if [[ $cid == $s ]]; then current=$cid break 2 fi done done # Fallback if no starred sink matched [[ -z $current ]] && current=${sinks[1]} # Locate index of current in sinks (zsh arrays are 1-based) idx=1 for i in {1..${#sinks[@]}}; do [[ ${sinks[$i]} == "$current" ]] && { idx=$i; break; } done # Compute next sink (wrap) next=${sinks[$(( (idx % ${#sinks}) + 1 ))]} # Switch default wpctl set-default "$next" >/dev/null 2>&1 || exit 1 echo "Switched to sink $next"