1#!/bin/sh2# __ _ _ _ _ _ _3# _ __ ___ / _(_) | |__ | |_ _ ___| |_ ___ ___ | |_| |__4# | '__/ _ \| |_| |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __| '_ \5# | | | (_) | _| |_____| |_) | | |_| | __/ || (_) | (_) | |_| | | |6# |_| \___/|_| |_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__|_| |_|7#8# Author: Nick Clyde (clydedroid)9#10# A script that generates a rofi menu that uses bluetoothctl to11# connect to bluetooth devices and display status info.12#13# Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu)14# Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl)15#16# Depends on:17# Arch repositories: rofi, bluez-utils (contains bluetoothctl), bc1819# Constants20divider="---------"21goback="Back"2223# Checks if bluetooth controller is powered on24power_on() {25 if bluetoothctl show | grep -q "Powered: yes"; then26 return 027 else28 return 129 fi30}3132# Toggles power state33toggle_power() {34 if power_on; then35 bluetoothctl power off36 show_menu37 else38 if rfkill list bluetooth | grep -q 'blocked: yes'; then39 rfkill unblock bluetooth && sleep 340 fi41 bluetoothctl power on42 show_menu43 fi44}4546# Checks if controller is scanning for new devices47scan_on() {48 if bluetoothctl show | grep -q "Discovering: yes"; then49 echo "Scan: on"50 return 051 else52 echo "Scan: off"53 return 154 fi55}5657# Toggles scanning state58toggle_scan() {59 if scan_on; then60 kill $(pgrep -f "bluetoothctl --timeout 5 scan on")61 bluetoothctl scan off62 show_menu63 else64 bluetoothctl --timeout 5 scan on65 echo "Scanning..."66 show_menu67 fi68}6970# Checks if controller is able to pair to devices71pairable_on() {72 if bluetoothctl show | grep -q "Pairable: yes"; then73 echo "Pairable: on"74 return 075 else76 echo "Pairable: off"77 return 178 fi79}8081# Toggles pairable state82toggle_pairable() {83 if pairable_on; then84 bluetoothctl pairable off85 show_menu86 else87 bluetoothctl pairable on88 show_menu89 fi90}9192# Checks if controller is discoverable by other devices93discoverable_on() {94 if bluetoothctl show | grep -q "Discoverable: yes"; then95 echo "Discoverable: on"96 return 097 else98 echo "Discoverable: off"99 return 1100 fi101}102103# Toggles discoverable state104toggle_discoverable() {105 if discoverable_on; then106 bluetoothctl discoverable off107 show_menu108 else109 bluetoothctl discoverable on110 show_menu111 fi112}113114# Checks if a device is connected115device_connected() {116 device_info=$(bluetoothctl info "$1")117 if echo "$device_info" | grep -q "Connected: yes"; then118 return 0119 else120 return 1121 fi122}123124# Toggles device connection125toggle_connection() {126 if device_connected "$1"; then127 bluetoothctl disconnect "$1"128 device_menu "$device"129 else130 bluetoothctl connect "$1"131 device_menu "$device"132 fi133}134135# Checks if a device is paired136device_paired() {137 device_info=$(bluetoothctl info "$1")138 if echo "$device_info" | grep -q "Paired: yes"; then139 echo "Paired: yes"140 return 0141 else142 echo "Paired: no"143 return 1144 fi145}146147# Toggles device paired state148toggle_paired() {149 if device_paired "$1"; then150 bluetoothctl remove "$1"151 device_menu "$device"152 else153 bluetoothctl pair "$1"154 device_menu "$device"155 fi156}157158# Checks if a device is trusted159device_trusted() {160 device_info=$(bluetoothctl info "$1")161 if echo "$device_info" | grep -q "Trusted: yes"; then162 echo "Trusted: yes"163 return 0164 else165 echo "Trusted: no"166 return 1167 fi168}169170# Toggles device connection171toggle_trust() {172 if device_trusted "$1"; then173 bluetoothctl untrust "$1"174 device_menu "$device"175 else176 bluetoothctl trust "$1"177 device_menu "$device"178 fi179}180181# Backwards-compatible function to print paired devices.182paired_devices() {183 if [ -n "$(bluetoothctl version | cut -d ' ' -f 2 | awk -F. '($1 > 5 || $1 == 5 && $2 > 64)')" ]; then184 bluetoothctl devices Paired185 else186 bluetoothctl paired-devices187 fi188}189190191# Prints a short string with the current bluetooth status192# Useful for status bars like polybar, etc.193print_status() {194 if power_on; then195 printf ''196 sep=""197 paired_devices | grep Device | cut -d ' ' -f 2 |198 while IFS= read -r device; do199 if device_connected "$device"; then200 device_alias=$(bluetoothctl info "$device" | grep "Alias" | cut -d ' ' -f 2-)201 printf "$sep %s" "$device_alias"202 sep=","203 fi204 done205 printf "\n"206 else207 echo ""208 fi209}210211# A submenu for a specific device that allows connecting, pairing, and trusting212device_menu() {213 device=$1214215 # Get device name and mac address216 device_name=$(echo "$device" | cut -d ' ' -f 3-)217 mac=$(echo "$device" | cut -d ' ' -f 2)218219 # Build options220 if device_connected "$mac"; then221 connected="Connected: yes"222 else223 connected="Connected: no"224 fi225 paired=$(device_paired "$mac")226 trusted=$(device_trusted "$mac")227228 # Open rofi menu, read chosen option229 chosen="$(printf "%s\n%s\n%s\n%s\n%s\nExit\n" "$connected" "$paired" "$trusted" "$divider" "$goback" \230 | $rofi_command "$device_name")"231232 # Match chosen option to command233 case "$chosen" in234 "" | "$divider")235 echo "No option chosen."236 ;;237 "$connected")238 toggle_connection "$mac"239 ;;240 "$paired")241 toggle_paired "$mac"242 ;;243 "$trusted")244 toggle_trust "$mac"245 ;;246 "$goback")247 show_menu248 ;;249 esac250}251252# Opens a rofi menu with current bluetooth status and options to connect253show_menu() {254 # Get menu options255 if power_on; then256 power="Power: on"257258 # Human-readable names of devices, one per line259 # If scan is off, will only list paired devices260 devices=$(bluetoothctl devices | grep Device | cut -d ' ' -f 3-)261262 # Get controller flags263 scan=$(scan_on)264 pairable=$(pairable_on)265 discoverable=$(discoverable_on)266267 # Open rofi menu, read chosen option268 chosen="$(printf "%s\n%s\n%s\n%s\n%s\n%s\nExit\n" \269 "$devices" "$divider" "$power" "$scan" "$pairable" "$discoverable" \270 | $rofi_command "Bluetooth")"271 else272 power="Power: off"273 # Open rofi menu, read chosen option274 chosen="$(printf "%s\nExit\n" "$power" | $rofi_command "Bluetooth")"275 fi276277278 # Match chosen option to command279 case "$chosen" in280 "" | "$divider")281 echo "No option chosen."282 ;;283 "$power")284 toggle_power285 ;;286 "$scan")287 toggle_scan288 ;;289 "$discoverable")290 toggle_discoverable291 ;;292 "$pairable")293 toggle_pairable294 ;;295 *)296 device=$(bluetoothctl devices | grep "$chosen")297 # Open a submenu if a device is selected298 if [ "$device" ]; then device_menu "$device"; fi299 ;;300 esac301}302303# Rofi command to pipe into, can add any options here304rofi_command="rofi -dmenu $* -p"305306case "$1" in307 --status)308 print_status309 ;;310 *)311 show_menu312 ;;313esac