#!/usr/bin/env bash # __ _ _ _ _ _ _ # _ __ ___ / _(_) | |__ | |_ _ ___| |_ ___ ___ | |_| |__ # | '__/ _ \| |_| |_____| '_ \| | | | |/ _ \ __/ _ \ / _ \| __| '_ \ # | | | (_) | _| |_____| |_) | | |_| | __/ || (_) | (_) | |_| | | | # |_| \___/|_| |_| |_.__/|_|\__,_|\___|\__\___/ \___/ \__|_| |_| # # Author: Nick Clyde (clydedroid) # # A script that generates a rofi menu that uses bluetoothctl to # connect to bluetooth devices and display status info. # # Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu) # Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl) # # Depends on: # Arch repositories: rofi, bluez-utils (contains bluetoothctl) # slightly modified for style and function by daylinmorgan # Constants divider="---------" goback="Back" styles="$(dirname $(which $0))/../styles" rofi_cmd="rofi -theme $styles/bluetooth.rasi -dmenu -p" # Checks if bluetooth controller is powered on power_on() { if bluetoothctl show | grep -q "Powered: yes"; then return 0 else return 1 fi } # Toggles power state toggle_power() { if power_on; then bluetoothctl power off else if rfkill list bluetooth | grep -q 'blocked: yes'; then rfkill unblock bluetooth && sleep 3 fi bluetoothctl power on fi } # Checks if controller is scanning for new devices scan_on() { if bluetoothctl show | grep -q "Discovering: yes"; then echo "Scan: on" return 0 else echo "Scan: off" return 1 fi } # Toggles scanning state toggle_scan() { if scan_on; then kill $(pgrep -f "bluetoothctl scan on") bluetoothctl scan off & notify-send -u low -t 5000 "Scanning disabled" else bluetoothctl scan on & notify-send -u low -t 5000 "Scanning enabled" sleep 5 fi } # Checks if controller is able to pair to devices pairable_on() { if bluetoothctl show | grep -q "Pairable: yes"; then echo "Pairable: on" return 0 else echo "Pairable: off" return 1 fi } # Toggles pairable state toggle_pairable() { if pairable_on; then bluetoothctl pairable off notify-send -u low -t 5000 "Pairing disabled" else bluetoothctl pairable on notify-send -u low -t 5000 "Pairing enabled" fi } # Checks if controller is discoverable by other devices discoverable_on() { if bluetoothctl show | grep -q "Discoverable: yes"; then echo "Discoverable: on" return 0 else echo "Discoverable: off" return 1 fi } # Toggles discoverable state toggle_discoverable() { if discoverable_on; then bluetoothctl discoverable off notify-send -u low -t 5000 "Discoverable disabled" else bluetoothctl discoverable on notify-send -u low -t 5000 "Discoverable enabled" fi } # Checks if a device is connected device_connected() { device_info=$(bluetoothctl info "$1") if echo "$device_info" | grep -q "Connected: yes"; then return 0 else return 1 fi } # Toggles device connection toggle_connection() { if device_connected $1; then bluetoothctl disconnect $1 notify-send -u low -t 5000 "Disconnecting $1" device_menu "$device" else bluetoothctl connect $1 notify-send -u low -t 5000 "Connecting $1" device_menu "$device" fi } # Checks if a device is paired device_paired() { device_info=$(bluetoothctl info "$1") if echo "$device_info" | grep -q "Paired: yes"; then echo "Paired: yes" return 0 else echo "Paired: no" return 1 fi } # Toggles device paired state toggle_paired() { if device_paired $1; then bluetoothctl remove $1 device_menu "$device" notify-send -u low -t 5000 "Removing $1" else bluetoothctl pair $1 device_menu "$device" notify-send -u low -t 5000 "Pairing $1" fi } # Checks if a device is trusted device_trusted() { device_info=$(bluetoothctl info "$1") if echo "$device_info" | grep -q "Trusted: yes"; then echo "Trusted: yes" return 0 else echo "Trusted: no" return 1 fi } # Toggles device connection toggle_trust() { if device_trusted $1; then bluetoothctl untrust $1 device_menu "$device" notify-send -u low -t 5000 "Untrusting $1" else bluetoothctl trust $1 device_menu "$device" notify-send -u low -t 5000 "Trusting $1" fi } # Prints a short string with the current bluetooth status # Useful for status bars like polybar, etc. print_status() { if power_on; then printf '' mapfile -t paired_devices < <(bluetoothctl paired-devices | grep Device | cut -d ' ' -f 2) counter=0 for device in "${paired_devices[@]}"; do if device_connected $device; then device_alias=$(bluetoothctl info $device | grep "Alias" | cut -d ' ' -f 2-) if [ $counter -gt 0 ]; then printf ", %s" "$device_alias" else printf " %s" "$device_alias" fi ((counter++)) fi done printf "\n" else echo "" fi } # A submenu for a specific device that allows connecting, pairing, and trusting device_menu() { device=$1 # Get device name and mac address device_name=$(echo $device | cut -d ' ' -f 3-) mac=$(echo $device | cut -d ' ' -f 2) # Build options if device_connected $mac; then connected="Connected: yes" else connected="Connected: no" fi paired=$(device_paired $mac) trusted=$(device_trusted $mac) options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit" # Open rofi menu, read chosen option chosen="$(echo -e "$options" | $rofi_cmd "$device_name")" # Match chosen option to command case $chosen in "" | $divider) echo "No option chosen." ;; Exit) exit ;; $connected) toggle_connection $mac ;; $paired) toggle_paired $mac ;; $trusted) toggle_trust $mac ;; $goback) # show_menu ;; esac } # # Opens a rofi menu with current bluetooth status and options to connect # show_menu() { # # Get menu options # if power_on; then # power="Power: on" # # Human-readable names of devices, one per line # # If scan is off, will only list paired devices # devices=$(bluetoothctl devices | grep Device | cut -d ' ' -f 3-) # # Get controller flags # scan=$(scan_on) # pairable=$(pairable_on) # discoverable=$(discoverable_on) # # Options passed to rofi # options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" # else # power="Power: off" # options="$power\nExit" # fi # # notify-send -u low -t 1000 -i "bluetooth" "main menu" # # Open rofi menu, read chosen option # chosen="$(echo -e "$options" | $rofi_command "Bluetooth")" # # Match chosen option to command # case $chosen in # "" | $divider) # echo "No option chosen." # ;; # $power) # toggle_power # ;; # $scan) # toggle_scan # ;; # $discoverable) # toggle_discoverable # ;; # $pairable) # toggle_pairable # ;; # *) # device=$(bluetoothctl devices | grep "$chosen") # # Open a submenu if a device is selected # if [[ $device ]]; then device_menu "$device"; fi # ;; # esac # } # Rofi command to pipe into, can add any options here # rofi_command="rofi -dmenu -theme $rofi_styles/bluetooth.rasi -no-fixed-num-lines -yoffset -100 -i -p " while true; do if power_on; then power="Power: on" # Human-readable names of devices, one per line # If scan is off, will only list paired devices devices='' while read -r device; do devices+="$device\n" done < <(bluetoothctl devices | grep Device | cut -d ' ' -f 3-) # devices=$(bluetoothctl devices | grep Device | cut -d ' ' -f 3-) # Get controller flags scan=$(scan_on) pairable=$(pairable_on) discoverable=$(discoverable_on) # Options passed to rofi # devices has baked in trailing newline if it exists options="$devices$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" else power="Power: off" options="$power\nExit" fi # Open rofi menu, read chosen option # chosen="$(echo -e "$options" | $rofi_command "Bluetooth" &)" chosen=$(echo -e $options | $rofi_cmd "Bluetooth") # Match chosen option to command case $chosen in "" | $divider) echo "No option chosen." ;; Exit) break ;; $power) toggle_power ;; $scan) toggle_scan ;; $discoverable) toggle_discoverable ;; $pairable) toggle_pairable ;; *) device=$(bluetoothctl devices | grep "$chosen") # Open a submenu if a device is selected if [[ $device ]]; then device_menu "$device"; fi ;; esac done