Alexander Bocken
dfac83a064
This will lead to less accurate behaviour with many devices listed in bluetoothctl devices but otherwise the script becomes unbearably slow. Remove head from the start up process if you wish to disable this feature
237 lines
9.2 KiB
Bash
Executable File
237 lines
9.2 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
|
|
|
|
#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}' )"
|
|
[ "$powerstatus" = "no" ] && [ "$1" = on ] && bluetoothctl power on
|
|
[ "$powerstatus" = "yes" ] && [ "$1" = off ] && bluetoothctl power off
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
#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
|
|
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" )))"
|
|
if [ "$sleep_period" -eq 1 ]; then
|
|
notify-send "Bluetooth" "Searching for devices, please wait 1 second"
|
|
else
|
|
notify-send "Bluetooth" "Searching for devices, please wait $sleep_period seconds"
|
|
fi
|
|
|
|
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 [ "$( grep "$dev_id" "$PAIRLIST")" = "" ] || [ "$(wc -l "$PAIRLIST")" -eq 0 ] && $pair_succesful
|
|
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
|