318 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
#!/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)
 | 
						|
 | 
						|
# Constants
 | 
						|
divider="---------"
 | 
						|
goback="Back"
 | 
						|
 | 
						|
# 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
 | 
						|
        show_menu
 | 
						|
    else
 | 
						|
        if rfkill list bluetooth | grep -q 'blocked: yes'; then
 | 
						|
            rfkill unblock bluetooth && sleep 3
 | 
						|
        fi
 | 
						|
        bluetoothctl power on
 | 
						|
        show_menu
 | 
						|
    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
 | 
						|
        show_menu
 | 
						|
    else
 | 
						|
        bluetoothctl scan on &
 | 
						|
        echo "Scanning..."
 | 
						|
        sleep 5
 | 
						|
        show_menu
 | 
						|
    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
 | 
						|
        show_menu
 | 
						|
    else
 | 
						|
        bluetoothctl pairable on
 | 
						|
        show_menu
 | 
						|
    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
 | 
						|
        show_menu
 | 
						|
    else
 | 
						|
        bluetoothctl discoverable on
 | 
						|
        show_menu
 | 
						|
    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
 | 
						|
        device_menu "$device"
 | 
						|
    else
 | 
						|
        bluetoothctl connect $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"
 | 
						|
    else
 | 
						|
        bluetoothctl pair $1
 | 
						|
        device_menu "$device"
 | 
						|
    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"
 | 
						|
    else
 | 
						|
        bluetoothctl trust $1
 | 
						|
        device_menu "$device"
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
# Prints a short string with the current bluetooth status
 | 
						|
# Useful for status bars like polybar, etc.
 | 
						|
print_status() {
 | 
						|
    if power_on; then
 | 
						|
        printf ''
 | 
						|
 | 
						|
        paired_devices_cmd="devices Paired"
 | 
						|
        # Check if an outdated version of bluetoothctl is used to preserve backwards compatibility
 | 
						|
        if (( $(echo "$(bluetoothctl version | cut -d ' ' -f 2) < 5.65" | bc -l) )); then
 | 
						|
            paired_devices_cmd="paired-devices"
 | 
						|
        fi
 | 
						|
 | 
						|
        mapfile -t paired_devices < <(bluetoothctl $paired_devices_cmd | 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_command "$device_name")"
 | 
						|
 | 
						|
    # Match chosen option to command
 | 
						|
    case $chosen in
 | 
						|
        "" | $divider)
 | 
						|
            echo "No option chosen."
 | 
						|
            ;;
 | 
						|
        $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
 | 
						|
 | 
						|
    # 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 -theme theme/passmenu.rasi -dmenu -no-fixed-num-lines -yoffset -100 -i -p"
 | 
						|
 | 
						|
case "$1" in
 | 
						|
    --status)
 | 
						|
        print_status
 | 
						|
        ;;
 | 
						|
    *)
 | 
						|
        show_menu
 | 
						|
        ;;
 | 
						|
esac
 |