summaryrefslogtreecommitdiff
path: root/bin/.bin/bluetooth.sh
blob: 5ae5281c0db594944472ffcec1ea17d8d4868490 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/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