248 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/sh
 | |
| SCAN_PERIOD=5
 | |
| AUTOTRUST=false #trust every newly paired device
 | |
| AUTOSCAN=false #immediately start scanning when started to speed up pairing process
 | |
| #If you want to update symbols in your status bar add your details below:
 | |
| bar="dwmblocks"
 | |
| barsignal="4"
 | |
| 
 | |
| #locations of blacklist and hard coded list of paired devices (watch out, need to modify Makefile as well if you want to change these values and still use make install)
 | |
| BLACKLIST=${XDG_DATA_HOME:-$HOME/.local/share}/bt/blacklist
 | |
| PAIRLIST=${XDG_DATA_HOME:-$HOME/.local/share}/bt/paired
 | |
| 
 | |
| actions="pair
 | |
| unpair"
 | |
| 
 | |
| [ "$AUTOTRUST" = false ] && actions="${actions}
 | |
| trust"
 | |
| 
 | |
| #Checks for necessary programs to be present. Very unlikely not to be present but let's just err on the safer side.
 | |
| for prog in dmenu bluetoothctl awk cat date nl; do
 | |
|    if ! hash "$prog" 2>/dev/null; then
 | |
|       printf 'bthandler: %s: command not found\n' "$prog" >&2
 | |
|       exit 127
 | |
|    fi
 | |
| done
 | |
| 
 | |
| power(){
 | |
| 	powerstatus="$( bluetoothctl show | grep Powered | awk '{print $2}' )"
 | |
| 	if [ "$powerstatus" = "no" ]; then
 | |
| 		[ "$1" = on ] && bluetoothctl power on
 | |
| 	elif [ "$powerstatus" = "yes" ]; then
 | |
| 		[ "$1" = off ] && bluetoothctl power off
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| scan(){
 | |
| 	scanstatus="$( bluetoothctl show | grep Discovering | awk '{print $2}' )"
 | |
| 	if [ "$1" = on ]; then
 | |
| 		#sets variable in case scanning was no already on before the start of bt
 | |
| 		[ -f /tmp/bt_start_scan$$ ] || start_scan="$( date +'%s' )"
 | |
| 		if [ "$scanstatus" = "no" ]; then
 | |
| 			bluetoothctl scan on &
 | |
| 			start_scan="$( date +'%s' )"
 | |
| 		fi
 | |
| 		echo "$start_scan" > /tmp/bt_start_scan$$
 | |
| 	elif [ "$1" = off ]; then
 | |
| 		if [ "$scanstatus" = "yes" ]; then
 | |
| 			bluetoothctl scan off
 | |
| 		fi
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| #Check bluetoothctl paired-devices for new devices to be hardcoded into pair list
 | |
| update_pair_list(){
 | |
| 	btctl_paired_devices="$(bluetoothctl paired-devices)"
 | |
| 	if [ -n "$btctl_paired_devices" ]
 | |
| 	then
 | |
| 		btctl_not_in_list="$( echo "$btctl_paired_devices" | grep -vf "$PAIRLIST")"
 | |
| 		[ -n "$btctl_not_in_list" ] && echo "$btctl_not_in_list" >> "$PAIRLIST"
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| 
 | |
| #start scanning as early as possible to speed up pairing process
 | |
| startup(){
 | |
| 	#remove old tmp files
 | |
| 	for f in /tmp/*
 | |
| 	do
 | |
| 	  case $f in
 | |
| 	    /tmp/bt_start_scan*) rm "$f";;
 | |
| 	    *) true;;
 | |
| 	  esac
 | |
| 	done
 | |
| 	if $AUTOSCAN
 | |
| 	then
 | |
| 		power on
 | |
| 		scan on
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| 
 | |
| if [ "$1" = "edit" ]; then
 | |
| 	case $2 in
 | |
| 		"pairlist")${EDITOR:-vim} "$PAIRLIST" && exit;;
 | |
| 		"blacklist")${EDITOR:-vim} "$BLACKLIST" && exit;;
 | |
| 		*)true;;
 | |
| 		esac
 | |
| fi
 | |
| #Sadly needs to be run without '&' for now, since it otherwise breaks start_scan variable
 | |
| startup &
 | |
| update_pair_list &
 | |
| 
 | |
| # include head command here to speed up launch if too many devices are listed (long uptime and long scanning will lead to this depending on your surroundings)
 | |
| # shellcheck disable=SC1091
 | |
| if bluetoothctl devices | head | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -q "Connected: yes"
 | |
| then
 | |
| 	actions="$(printf "disconnect\n%s" "$actions")"
 | |
| fi
 | |
| 
 | |
| 
 | |
| #Compile list of all Bluetooth IDS of paired devices (from bluetoothctl and from hardcoded list)
 | |
| bt_IDS="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | sort -u | awk '{print $2}' )"
 | |
| 
 | |
| #Compile list of all device Names of paired devices (from bluetoothctl and from hardcoded list)
 | |
| paired_devices="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | sort -u | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
 | |
| 
 | |
| disp_devices="$( echo "$paired_devices" | grep -vf "$BLACKLIST"  )"
 | |
| 
 | |
| #detects current power mode of controller and adjusts options accordingly
 | |
| powerstatus="$( bluetoothctl show | grep Powered | awk '{print $2}' )"
 | |
| poweroption="$( echo "$powerstatus" | sed 's/yes/power off/; s/no/power on/' )"
 | |
| #Don't print empty device list, removes unnecessary empty choice in dmenu
 | |
| [ "$disp_devices" = "" ] && choice=$( printf "%s\n%s" "$actions" "$poweroption" | dmenu -i -p 'What BT action would you like to perform:' )
 | |
| [ "$disp_devices" != "" ] && choice=$( ( echo "$disp_devices" && printf "%s\n%s" "$actions" "$poweroption" ) | dmenu -i -p 'What BT action would you like to perform:' )
 | |
| 
 | |
| cleanup(){
 | |
| 	scan off
 | |
| 	rm /tmp/bt_start_scan$$ 2> /dev/null
 | |
| 	pkill -RTMIN+$barsignal $bar
 | |
| 	exit 0
 | |
| }
 | |
| 
 | |
| pair(){
 | |
| 	#since this function can get called indefinitely, make sure to always be scanning and controller has power in the case that it got deactived by some other process.
 | |
| 	power on
 | |
| 	scan on
 | |
| 	#check whether $SCAN_PERIOD seconds has already passed since starting scanning, if not, wait for the rest of that time.
 | |
| 	start_scan="$(cat /tmp/bt_start_scan$$)"
 | |
| 	if [ $(( $(date +'%s') - $start_scan )) -lt $SCAN_PERIOD ]; then
 | |
| 		sleep_period="$(( $SCAN_PERIOD - $( date +'%s') + $start_scan ))"
 | |
| 		[ "$sleep_period" -gt 1 ] && plural="s"
 | |
| 		notify-send "Bluetooth" "Searching for devices, please wait $sleep_period second$plural"
 | |
| 		sleep "$sleep_period"
 | |
| 	fi
 | |
| 	all_devices="$( bluetoothctl devices )"
 | |
| 	if [ "$paired_devices" = "" ]; then
 | |
| 		new_devices="$( echo "$all_devices" | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
 | |
| 		bt_IDS="$( echo "$all_devices" | awk '{print $2}' )"
 | |
| 	else
 | |
| 		#echo "$paired_devices" > /tmp/paired_devices$$
 | |
| 		filtered_devices="$( echo "$all_devices" | grep -v "$paired_devices")"
 | |
| 		bt_IDS="$( echo "$filtered_devices" | awk '{print $2}' )"
 | |
| 		new_devices="$( echo "$filtered_devices" | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
 | |
| 		#rm /tmp/paired_devices$$
 | |
| 	fi
 | |
| 	[ "$new_devices" = "" ] && options="rescan" || options=$(echo "$new_devices" && echo 'rescan')
 | |
| 	choice=$( echo "$options" | dmenu -l 10 -i -p 'pair with which device?' )
 | |
| 	if [ -n "$choice" ]; then
 | |
| 		if [ "$choice" = "rescan" ]; then
 | |
| 			start_scan="$( date +'%s')"
 | |
| 			echo "$start_scan" > /tmp/bt_start_scan$$
 | |
| 			pair
 | |
| 		else
 | |
| 			dev_no=$( echo "$new_devices" | nl | grep "$choice" | awk '{print $1}')
 | |
| 			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
 | |
| 			#only attempt to connect if pairing succeeds
 | |
| 			bluetoothctl pair "$dev_id" && ( pair_succesful=true && bluetoothctl connect "$dev_id" ) || pair_succesful=false
 | |
| 			if $AUTOTRUST; then
 | |
| 				bluetoothctl trust "$dev_id"
 | |
| 			fi
 | |
| 			#if device is not already hard coded as paired, add to paired devices list
 | |
| 			if $pair_succesful && [ "$( grep "$dev_id" "$PAIRLIST")" = "" ] || [ "$(wc -l "$PAIRLIST")" -eq 0 ]
 | |
| 			then
 | |
| 				echo to be added to "$PAIRLIST":
 | |
| 				echo Device "$dev_id" "$choice"
 | |
| 				echo Device "$dev_id" "$choice" >> "$PAIRLIST"
 | |
| 			fi
 | |
| 		fi
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| case $choice in
 | |
| 	"power on") bluetoothctl power on;;
 | |
| 	"power off") bluetoothctl power off;;
 | |
| 	"scan on") bluetoothctl power on && echo power on && sleep 2
 | |
| 		([ -n "$TERMINAL" ] && $TERMINAL -e bluetoothctl scan on ) || st bluetoothctl scan on;;
 | |
| 	"pair") pair;;
 | |
| 	"unpair") choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'remove which paired device?')
 | |
| 		if [ -n "$choice" ]; then
 | |
| 			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
 | |
| 			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
 | |
| 			bluetoothctl remove "$dev_id"
 | |
| 			#remove device to unpair from hard coded paired devices list
 | |
| 			new_paired_list="$( grep -v "$dev_id" "$PAIRLIST" )"
 | |
| 			if [ "$new_paired_list" != "" ]
 | |
| 			then
 | |
| 				echo "$new_paired_list" > "$PAIRLIST"
 | |
| 			else
 | |
| 				rm -rf "$PAIRLIST"
 | |
| 				touch "$PAIRLIST"
 | |
| 			fi
 | |
| 		fi;;
 | |
| 
 | |
| 	"trust")
 | |
| 		#search through all devices which are connected and only list those as options
 | |
| 		# shellcheck disable=SC1091
 | |
| 		untrusted_devices="$( awk '{print "bluetoothctl info "$2}' "$PAIRLIST" | . /dev/stdin | grep -E '(Alias:|Trusted:)' | sed -e  'N;s/\n/;/;s/^.?*Alias: //' | grep "Trusted: no" | awk -F ';' '{print $1}' )"
 | |
| 		if [ "$( echo "$untrusted_devices" | wc -l )" -gt 0 ]
 | |
| 		then
 | |
| 			choice=$( echo "$untrusted_devices" | dmenu -l 10 -i -p 'remove which paired device?')
 | |
| 		else
 | |
| 			notify-send "Bluetooth" "No paired devices that are not trusted"
 | |
| 		fi
 | |
| 		if [ -n "$choice" ]; then
 | |
| 			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
 | |
| 			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
 | |
| 			bluetoothctl trust "$dev_id"
 | |
| 		fi;;
 | |
| 	"disconnect")
 | |
| 		#search through all devices which are connected and only list those as options
 | |
| 		# shellcheck disable=SC1091
 | |
| 		connected_devices="$( bluetoothctl devices | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -E '(Alias:|Connected:)' | sed -e  'N;s/\n/;/;s/^.?*Alias: //' | grep "Connected: yes" | awk -F ';' '{print $1}' )"
 | |
| 		echo "$connected_devices"
 | |
| 		#only open dmenu prompt if there is more than one connected device
 | |
| 		if [ "$( echo "$connected_devices" | wc -l )" -gt 1 ]
 | |
| 		then
 | |
| 			choice=$( echo "$connected_devices" | dmenu -l 10 -i -p 'remove which paired device?')
 | |
| 	        else
 | |
| 			choice="$connected_devices"
 | |
| 		fi
 | |
| 		#only there was a choice (instead of canceling the dmenu)
 | |
| 		if [ -n "$choice" ]; then
 | |
| 			dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
 | |
| 			dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}' )
 | |
| 			bluetoothctl disconnect "$dev_id"
 | |
| 		fi;;
 | |
| 	"blacklist")
 | |
| 		choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'blacklist which paired device from selection?')
 | |
| 		if [ -n "$choice" ]; then
 | |
| 			echo "$choice" >> "$BLACKLIST"
 | |
| 		fi;;
 | |
| 	*)
 | |
| 		dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
 | |
| 		[ "$dev_no" != "" ] && dev_id=$( echo "$bt_IDS" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}')
 | |
| 		if [ -n "$dev_id" ]; then
 | |
| 			power on
 | |
| 			if bluetoothctl info "$dev_id" | grep -q "Connected: yes"
 | |
| 			then
 | |
| 				bluetoothctl disconnect "$dev_id"
 | |
| 			else
 | |
| 				bluetoothctl devices | grep -q "$dev_id" || bluetoothctl pair "$dev_id"
 | |
| 				bluetoothctl connect "$dev_id"
 | |
| 			fi
 | |
| 		fi;;
 | |
| esac
 | |
| 
 | |
| cleanup
 |