2020-07-05 15:21:24 +02:00
#!/bin/sh
SCAN_PERIOD=5
2020-07-05 19:45:12 +02:00
AUTOTRUST=false #trust every newly paired device
2020-07-25 21:43:10 +02:00
AUTOSCAN=false #immediately start scanning when started to speed up pairing process
2021-06-20 17:19:57 +02:00
#If you want to update symbols in your statusbar add your details below:
2020-09-30 15:33:47 +02:00
bar="dwmblocks"
barsignal="4"
2020-07-25 21:43:10 +02:00
2020-07-05 15:21:24 +02:00
#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)
2021-06-29 13:20:41 +02:00
BLACKLIST=${XDG_CONFIG_HOMe:-$HOME/.config}/bt/blacklist
PAIRLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/paired
ALIASLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/alias
2020-07-05 15:21:24 +02:00
2021-06-20 17:19:57 +02:00
actions="$(printf 'pair\nunpair\n')"
2020-07-05 15:21:24 +02:00
2021-06-20 17:19:57 +02:00
[ "$AUTOTRUST" = false ] && actions="$(printf '%s\ntrust\n' "$actions")"
2020-07-05 15:21:24 +02:00
#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}' )"
2021-01-01 22:47:16 +01:00
if [ "$powerstatus" = "no" ]; then
[ "$1" = on ] && bluetoothctl power on
elif [ "$powerstatus" = "yes" ]; then
[ "$1" = off ] && bluetoothctl power off
fi
2020-07-05 15:21:24 +02:00
}
2020-07-05 19:45:12 +02:00
2020-07-05 15:21:24 +02:00
scan(){
scanstatus="$( bluetoothctl show | grep Discovering | awk '{print $2}' )"
if [ "$1" = on ]; then
2020-07-05 19:45:12 +02:00
#sets variable in case scanning was no already on before the start of bt
2020-07-10 11:37:04 +02:00
[ -f /tmp/bt_start_scan$$ ] || start_scan="$( date +'%s' )"
2020-07-05 15:21:24 +02:00
if [ "$scanstatus" = "no" ]; then
2021-07-02 11:02:13 +02:00
bluetoothctl scan on &
2020-07-05 15:21:24 +02:00
start_scan="$( date +'%s' )"
fi
2020-07-10 11:37:04 +02:00
echo "$start_scan" > /tmp/bt_start_scan$$
2020-07-05 15:21:24 +02:00
elif [ "$1" = off ]; then
if [ "$scanstatus" = "yes" ]; then
2021-07-02 11:02:13 +02:00
bluetoothctl scan off
2020-07-05 15:21:24 +02:00
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(){
if $AUTOSCAN
then
power on
2021-06-29 16:40:32 +02:00
scan on > /dev/null
2020-07-05 15:21:24 +02:00
fi
}
2020-07-05 19:45:12 +02:00
2021-04-23 10:12:43 +02:00
if [ "$1" = "edit" ]; then
case $2 in
2021-06-28 20:10:16 +02:00
"pairlist")${EDITOR:-vim} "$PAIRLIST";;
"blacklist")${EDITOR:-vim} "$BLACKLIST";;
"aliaslist")${EDITOR:-vim} "$ALIASLIST";;
2021-04-23 10:12:43 +02:00
*)true;;
esac
2021-06-28 20:10:16 +02:00
exit
2021-04-23 10:12:43 +02:00
fi
2020-07-05 19:45:12 +02:00
#Sadly needs to be run without '&' for now, since it otherwise breaks start_scan variable
2020-07-10 11:37:04 +02:00
startup &
2020-07-05 15:21:24 +02:00
update_pair_list &
2021-07-02 11:02:13 +02:00
connected_ids="$(bluetoothctl devices | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -E '(^Device |Connected:)' | sed -e 'N;s/\n/;/' | grep 'Connected: yes' | cut -d' ' -f2,5)"
connected_devices="$(echo "$connected_ids" | while read -r id; do
if grep -q "$id" "$ALIASLIST"; then
grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
else
grep "$id" "$PAIRLIST" | cut -d' ' --complement -f1,2
fi
done)"
2020-07-05 15:21:24 +02:00
2021-07-02 11:02:13 +02:00
[ -z "$connected_devices" ] && actions="$(printf "disconnect\n%s" "$actions")"
2020-07-05 15:21:24 +02:00
#Compile list of all Bluetooth IDS of paired devices (from bluetoothctl and from hardcoded list)
2021-06-28 20:10:16 +02:00
bt_IDS="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | awk '{print $2}' | sort -u )"
2020-07-05 15:21:24 +02:00
#Compile list of all device Names of paired devices (from bluetoothctl and from hardcoded list)
2021-07-02 11:02:13 +02:00
paired_devices_with_id="$( ( bluetoothctl paired-devices && cat "$PAIRLIST" ) | sort -u | cut -d' ' --complement -f1)"
2021-06-28 20:10:16 +02:00
#de-alias
paired_devices="$(echo "$paired_devices_with_id" | while read -r device_line; do
id="$(echo "$device_line" | cut -d' ' -f1)"
if grep -q "$id" "$ALIASLIST"; then
grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
else
echo "$device_line" | cut -d' ' --complement -f1
fi
done)"
2021-06-20 17:19:57 +02:00
echo "paired_devices:$paired_devices"
2020-07-05 15:21:24 +02:00
2021-07-02 11:02:13 +02:00
disp_devices="$( echo "$paired_devices" | grep -vf "$BLACKLIST" )"
#show which devices are connected in menu
disp_devices_with_links="$(echo "$paired_devices" | while read -r device; do
printf '%s' "$device"
if echo "$device" | grep -q "$connected_devices"; then
printf '🔗'
fi
printf '\n'
done
)"
2020-07-05 15:21:24 +02:00
#detects current power mode of controller and adjusts options accordingly
2020-07-05 15:24:19 +02:00
powerstatus="$( bluetoothctl show | grep Powered | awk '{print $2}' )"
2020-07-05 15:21:24 +02:00
poweroption="$( echo "$powerstatus" | sed 's/yes/power off/; s/no/power on/' )"
#Don't print empty device list, removes unnecessary empty choice in dmenu
2021-07-02 11:02:13 +02:00
[ "$disp_devices" = "" ] && choice=$( printf "%s\n%s" "$actions" "$poweroption" | dmenu -i -p 'What BT action would you like to perform:' | tr -d '🔗')
[ "$disp_devices" != "" ] && choice=$( ( echo "$disp_devices_with_links" && printf "%s\n%s" "$actions" "$poweroption" ) | dmenu -i -p 'What BT action would you like to perform:' | tr -d '🔗')
2020-07-05 15:21:24 +02:00
cleanup(){
scan off
2021-06-29 16:22:41 +02:00
rm -f /tmp/bt_start_scan*
2020-09-30 15:33:47 +02:00
pkill -RTMIN+$barsignal $bar
2020-07-05 15:21:24 +02:00
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
2021-06-29 16:40:32 +02:00
scan on > /dev/null
2020-07-05 15:21:24 +02:00
#check whether $SCAN_PERIOD seconds has already passed since starting scanning, if not, wait for the rest of that time.
2020-07-10 11:37:04 +02:00
start_scan="$(cat /tmp/bt_start_scan$$)"
2021-04-17 15:45:40 +02:00
if [ $(( $(date +'%s') - $start_scan )) -lt $SCAN_PERIOD ]; then
sleep_period="$(( $SCAN_PERIOD - $( date +'%s') + $start_scan ))"
2021-01-01 22:47:16 +01:00
[ "$sleep_period" -gt 1 ] && plural="s"
2021-07-02 11:02:13 +02:00
notify-send "bt" "Searching for devices, please wait $sleep_period second$plural"
2020-07-05 15:21:24 +02:00
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
2020-07-05 19:45:12 +02:00
#echo "$paired_devices" > /tmp/paired_devices$$
filtered_devices="$( echo "$all_devices" | grep -v "$paired_devices")"
2020-07-05 15:21:24 +02:00
bt_IDS="$( echo "$filtered_devices" | awk '{print $2}' )"
new_devices="$( echo "$filtered_devices" | awk '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"
2020-07-05 19:45:12 +02:00
#rm /tmp/paired_devices$$
2020-07-05 15:21:24 +02:00
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')"
2020-07-10 11:37:04 +02:00
echo "$start_scan" > /tmp/bt_start_scan$$
2020-07-05 15:21:24 +02:00
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
2021-06-20 17:19:57 +02:00
if $pair_succesful && [ "$( grep "$dev_id" "$PAIRLIST")" = "" ] || [ "$(wc -l < "$PAIRLIST")" -eq 0 ]
2020-07-05 15:21:24 +02:00
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
2021-06-29 16:40:32 +02:00
([ -n "$TERMINAL" ] && $TERMINAL -e bluetoothctl scan on ) || st bluetoothctl scan on > /dev/null;;
2020-07-05 15:21:24 +02:00
"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;;
2020-07-06 12:39:22 +02:00
"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
2021-07-02 11:02:13 +02:00
notify-send "bt" "No paired devices that are not trusted"
2020-07-06 12:39:22 +02:00
fi
2020-07-05 15:21:24 +02:00
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;;
2020-07-06 12:32:16 +02:00
"disconnect")
2021-06-29 16:22:41 +02:00
#search through all devices and check which are connected
#only list those connceted devices as options
2020-07-06 12:32:16 +02:00
# shellcheck disable=SC1091
2021-06-29 14:32:36 +02:00
connected_ids="$(bluetoothctl devices | awk '{print "bluetoothctl info "$2}' | . /dev/stdin | grep -E '(^Device |Connected:)' | sed -e 'N;s/\n/;/' | grep 'Connected: yes' | cut -d' ' -f2,5)"
connected_devices="$(echo "$connected_ids" | while read -r id; do
if grep -q "$id" "$ALIASLIST"; then
grep "$id" "$ALIASLIST" | cut -d' ' --complement -f1
else
grep "$id" "$PAIRLIST" | cut -d' ' --complement -f1,2
fi
done)"
2020-07-06 12:32:16 +02:00
#only open dmenu prompt if there is more than one connected device
if [ "$( echo "$connected_devices" | wc -l )" -gt 1 ]
then
2021-06-29 14:32:36 +02:00
choice=$( echo "$connected_devices" | dmenu -l 10 -i -p 'disconnect which paired device?')
2020-07-06 12:32:16 +02:00
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;;
2020-07-05 15:21:24 +02:00
"blacklist")
choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'blacklist which paired device from selection?')
if [ -n "$choice" ]; then
echo "$choice" >> "$BLACKLIST"
fi;;
*)
2021-07-02 11:02:13 +02:00
echo "choice: $choice"
2020-07-05 15:21:24 +02:00
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}')
2021-07-02 11:02:13 +02:00
echo "dev_id: $dev_id"
2020-07-05 15:21:24 +02:00
if [ -n "$dev_id" ]; then
power on
2020-07-23 20:47:11 +02:00
if bluetoothctl info "$dev_id" | grep -q "Connected: yes"
then
2021-07-02 11:02:13 +02:00
echo Was connceted... disconnecting
2020-07-23 20:47:11 +02:00
bluetoothctl disconnect "$dev_id"
else
2021-07-02 11:02:13 +02:00
if ! bluetoothctl paired-devices | grep -q "$dev_id"; then
echo "not in paired-devices list, re-adding (device needs to be in pairing mode for this)"
notify-send "bt" "device not in paired-devices list, re-adding (device needs to be in pairing mode for this)"
scan on
echo "Scan on"
sleep 5
echo Attempting to pair...
2021-06-29 16:33:08 +02:00
bluetoothctl pair "$dev_id"
2021-07-02 11:02:13 +02:00
scan off
2021-06-29 16:33:08 +02:00
fi
2021-07-02 11:02:13 +02:00
echo connecting...
2020-07-23 20:47:11 +02:00
bluetoothctl connect "$dev_id"
fi
2020-07-05 15:21:24 +02:00
fi;;
esac
2020-07-10 11:39:13 +02:00
cleanup