#!/usr/bin/env bash set -uo pipefail # Keep -e off so our self-healing logic can handle error codes safely # Define the Rofi menu layout. declare -a order=( "⚡ Power On Bluetooth" "🛑 Power Off Bluetooth" "🔌 Disconnect All Devices" "---" "Speaker" "Earphones" "Earphones Left" "Living Room" ) # Map profiles to hardware MAC addresses. declare -A devices devices[Speaker]="A0:E9:DB:0E:43:79" devices[Earphones]="98:52:3D:F2:6E:3E" devices["Earphones Left"]="98:52:3D:F2:36:78" devices["Living Room"]="42:46:9F:D0:2C:53" # Prompt User via Rofi. MENU_OPTIONS=$(printf "%s\n" "${order[@]}") SELECTION=$(echo "$MENU_OPTIONS" | rofi -dmenu -p "Bluetooth Menu:") # Exit gracefully if the user hits Escape. if [ -z "$SELECTION" ] || [ "$SELECTION" = "---" ]; then exit 0 fi if [ "$SELECTION" = "⚡ Power On Bluetooth" ]; then notify-send "BT" "Powering adapter on..." bluetoothctl power on exit 0 fi if [ "$SELECTION" = "🛑 Power Off Bluetooth" ]; then notify-send "BT" "Powering adapter off..." bluetoothctl power off exit 0 fi if [ "$SELECTION" = "🔌 Disconnect All Devices" ]; then notify-send "BT" "Clearing all active connections..." for name in "${!devices[@]}"; do bluetoothctl disconnect "${devices[$name]}" >/dev/null 2>&1 || true done notify-send "BT" "All devices disconnected." exit 0 fi if [ -z "${devices[$SELECTION]:-}" ]; then notify-send "ERROR" "Invalid choice: $SELECTION" exit 1 fi TARGET_MAC="${devices[$SELECTION]}" # Ensure controller is actually powered up before trying to connect. if ! bluetoothctl show | grep -q "Powered: yes"; then notify-send "BT" "Waking up Bluetooth adapter..." bluetoothctl power on sleep 2 fi # Force controller runtime options to accept incoming passthroughs. bluetoothctl pairable on >/dev/null 2>&1 || true bluetoothctl discoverable on >/dev/null 2>&1 || true # Disconnect existing targets to free up the system audio pipelines. for name in "${!devices[@]}"; do bluetoothctl disconnect "${devices[$name]}" >/dev/null 2>&1 || true done # Query the machine's local hardware database cache. DEVICE_CHECK=$(bluetoothctl info "$TARGET_MAC" 2>&1 || true) if echo "$DEVICE_CHECK" | grep -q "not available"; then notify-send "BT: Provisioning" "Device missing from system cache. Probing airwaves..." # Run a targeted background scan pass to register device metadata. bluetoothctl --timeout 5 scan on >/dev/null 2>&1 & SCAN_PID=$! sleep 4 kill "$SCAN_PID" 2>/dev/null || true wait "$SCAN_PID" 2>/dev/null || true sleep 1 notify-send "BT: Provisioning" "Exchanging encryption keys..." bluetoothctl pair "$TARGET_MAC" bluetoothctl trust "$TARGET_MAC" fi # Attempt targeted connection sequence. notify-send "BT Switch" "Routing audio to $SELECTION..." CONNECT_OUT=$(bluetoothctl connect "$TARGET_MAC" 2>&1 || true) echo "$CONNECT_OUT" if echo "$CONNECT_OUT" | grep -q "Connection successful"; then bluetoothctl trust "$TARGET_MAC" >/dev/null 2>&1 || true # WirePlumber Sinks Syncing Engine. CLEAN_MAC="${TARGET_MAC//:/_}" SINK_NAME=$(wpctl status | grep -o "bluez_output\.[^\ ]*${CLEAN_MAC}[^\ ]*" | head -n 1 || true) if [ ! -z "$SINK_NAME" ]; then wpctl set-default "$SINK_NAME" # Set a safe volumne just in case we connect to earphones. wpctl set-volume "$SINK_NAME" 0.10 fi notify-send "BT Switch" "$SELECTION connected successfully!" # Handle key synchronization issues seamlessly.... elif echo "$CONNECT_OUT" | grep -q "br-connection-key-missing"; then notify-send "BT Error" "Key mismatch! Purging and re-pairing..." bluetoothctl remove "$TARGET_MAC" >/dev/null 2>&1 || true sleep 1 bluetoothctl --timeout 5 scan on >/dev/null 2>&1 & SCAN_PID=$! sleep 4 kill "$SCAN_PID" 2>/dev/null || true wait "$SCAN_PID" 2>/dev/null || true sleep 1 notify-send "BT Recovery" "Put device in pairing mode!" bluetoothctl pair "$TARGET_MAC" bluetoothctl trust "$TARGET_MAC" if bluetoothctl connect "$TARGET_MAC"; then notify-send "BT Switch" "$SELECTION repaired and connected!" else notify-send "BT ERROR" "Repair fallback routing failed." fi else notify-send "BT ERROR" "Failed to link with $SELECTION. Check pairing state or other devices." fi