Compare commits

...

No commits in common. "assets" and "master" have entirely different histories.

6 changed files with 510 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Alexander Bocken
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

33
Makefile Normal file
View File

@ -0,0 +1,33 @@
# bthandler
# See LICENSE file for copyright and license details.
# install this via `make install`, not with sudo
# paths
DESTDIR = /usr/local/bin
DATA_DIR = ~/.config/bt#if changed, needs adjustment in bt as well (defined in the beginning)
SRC = bt
install:
mkdir -p $(DESTDIR)
mkdir -p $(DATA_DIR)
sudo cp -f bt $(DESTDIR)/bt
sudo chmod 755 $(DESTDIR)/bt
touch $(DATA_DIR)/blacklist
touch $(DATA_DIR)/paired
touch $(DATA_DIR)/alias
cp config $(DATA_DIR)/config
uninstall:
sudo rm -f $(DESTDIR)/bt
rm -rf $(DATA_DIR)
#clears manual paired devices list and blacklist
clear:
rm -rf $(DATA_DIR)
mkdir -p $(DATA_DIR)
touch $(DATA_DIR)/paired
touch $(DATA_DIR)/blacklist
touch $(DATA_DIR)/alias
cp config $(DATA_DIR)/config
.PHONY: install uninstall clear

102
README.md Normal file
View File

@ -0,0 +1,102 @@
# bthandler
A simple interactive tool to:
- connect to already paired devices
- disconnect bluetooth devices
- pair new ones
- unpair already paired devices
- turn bluetooth on/off
- blacklist paired devices to not be listed in connection selection
- trust devices
- create alias names for devices
all via dmenu. Should be easily extendable by editing the `actions` string.
<img src="/../assets/cropped.png" alt="cropped preview" width="100%">
# Installation
Edit the Makefile to reflect your preffered installation destination. Then, simply
```bash
make install
```
*Warning*: `sudo make install` will not install correctly. Will be fixed in the future.
Afterwards, invoking `bt` will start the menu.
# Clear blacklist/pairlist
Since `bluetoothctl paired-devices` seems to be hugely unreliable in listing paired devices, bthandler has a seperate list for all devices paired through bthandler.
There is also a blacklist available to not display certain devices, which might be useful for autoconnection Bluetooth mice for example.
To clear these files, simply run
```bash
make clear
```
and these files will be reset to their inital, empty state.
**Note:** If for some reason `bluetoothctl paired-devices` works for you dont worry, bthandler lists all devices returned from `bluetoothctl` and it's own paired devices list. (Duplicates are not displayed.)
# Uninstall
To uninstall you might follow that it's a simple
```bash
make uninstall
```
If you have troubles with an uninstall, _additionally_ run `which bt | xargs rm`.
# files created by bthandler
There are four files which bthandler looks at, all located in `~/.config/bt` (also respects `$XDG_CONFIG_HOME`, if set):
- `config`
- `paired`
- `alias`
- `blacklist`
They're configurable via `bt edit <file>` or by navigating to the corresponding location manually.
## config
A shell file that gets read at the beginning of every invocation of bt. Mostly used to set variables such as the scan period, whether to automatically trust devices, etc..
Also defines the location of the following three files.
## paired
A hardcoded list of all paired devices. Gets updated even if devices are not paired through bt. This exists mostly because of historical unreliable output of `bluetoothctl paired-devices`. The syntax is exactly the same as that command:
```
Device <MAC-adress> <name>
```
With spaces as delimiter.
## alias
A file where one can manually set alias names for specific devices. Useful if multiple devices of the same device name are used on the PC.
The syntax is:
```
<MAC-adress> <new alias name>
```
With spaces as delimiter.
## blacklist
A list of devices not to list in bt even though they're paired. Useful for devices that usually automatically connect.
The syntax is:
```
<alias name/name>
```
Where the alias name takes precedence if present.
# Troubleshooting
## If bt takes long to show a menu
consider turning off `AUTOSCAN`. If there are many devices around you autoscanning will add them all to your `bluetoothctl devices` list. Every device on that list get's checked whether it's connected at startup.
This should only be an issue if `bluetoothctl scan off` is unreliable for you though.
# TODO
- [x] start scanning for new devices immediately at execution to save time when pairing new devices
- [x] auto-trust newly paired devices/trust device via dmenu (changeable via the AUTOTRUST variable in the beginning of bt, menu display adjusts accordingly)
- [x] blacklist devices via dmenu (action is hidden, but typing `blacklist` as your choice will reveal the menu)
- [x] update bthandler internal paired devices list if something new shows up via `bluetoothctl paired-devices` not already listed in the paired devices file
- [x] only show disconnect option if devices are already connected
- [x] only list connected devices to disconnect from, not all paired devices
- [x] only list not trusted devices in trust menu
- [x] selecting already connected device disconnects it
- [x] create alias for devices via dmenu

339
bt Executable file
View File

@ -0,0 +1,339 @@
#!/bin/sh
#load config
CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/bt/config"
#shellcheck disable=SC1090
. "$CONFIG"
"$AUTOTRUST" || actions="$(printf '%s\ntrust\n' "$actions")"
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 devices Paired for new devices to be hardcoded into pair list
update_pair_list(){
btctl_paired_devices="$(bluetoothctl devices Paired)"
if [ -n "$btctl_paired_devices" ]
then
#do not modify files when issues occur
if echo "$btctl_paired_devices" | grep -q "No default controller available"; then
return
fi
#needs temp var as it writes into the same file as it reads from
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
scan on
fi
}
if [ "$1" = "edit" ]; then
case $2 in
"config")${EDITOR:-vim} "$CONFIG";;
"paired")${EDITOR:-vim} "$PAIRLIST";;
"alias")${EDITOR:-vim} "$ALIASLIST";;
"blacklist")${EDITOR:-vim} "$BLACKLIST";;
*)echo "options: config/paired/alias/blacklist" && exit 1;;
esac
exit
fi
startup &
update_pair_list &
# shellcheck disable=SC1091
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)"
de_alias(){
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
}
[ -z "$connected_ids" ] ||
connected_devices="$( echo "$connected_ids" | de_alias )"
# shellcheck disable=SC2154
"$include_disconnect_option" &&
[ -n "$connected_devices" ] &&
actions="$(printf "disconnect\n%s" "$actions")"
#Compile list of all device ids of paired devices (from bluetoothctl and from hardcoded list)
connected_ids="$( ( bluetoothctl devices Paired && cat "$PAIRLIST" ) | sort -u | cut -d' ' -f2)"
paired_devices="$( echo "$connected_ids" | de_alias )"
echo "paired_devices:"
echo "$paired_devices"
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 [ -n "$connected_devices" ] &&
echo "$device" | grep -q "$connected_devices"
then
printf '🔗'
fi
printf '\n'
done
)"
#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/' )"
#read cmd arguments if available
if [ -z "$1" ]; then
#Don't print empty device list, removes unnecessary empty choice in dmenu
if [ "$disp_devices" = "" ];then
choice=$( printf "%s\n%s" "$actions" "$poweroption" |
dmenu -i -p 'What BT action would you like to perform:')
else
choice=$( ( echo "$disp_devices_with_links" && printf "%s\n%s" "$actions" "$poweroption" ) |
dmenu -i -p 'What BT action would you like to perform:' | sed 's/🔗$//')
fi
else
choice="$1"
fi
cleanup(){
scan off
rm -f /tmp/bt_start_scan*
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 > /dev/null
#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 "bt" "Searching for devices, please wait $sleep_period second$plural"
sleep "$sleep_period"
unset plural
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
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}' )"
fi
[ "$new_devices" = "" ] && options="rescan" || options=$(echo "$new_devices" && echo 'rescan')
if [ -z "$2" ]; then
choice=$( echo "$options" | dmenu -l 10 -i -p 'pair with which device?' )
else
if echo "$options" | grep -q "$1"; then
choice="$1"
else
echo "Device $1 not found." > /dev/stderr
exit 1
fi
fi
if [ -n "$choice" ]; then
if [ "$choice" = "rescan" ]; then
start_scan="$( date +'%s')"
echo "$start_scan" > /tmp/bt_start_scan$$
pair "$1"
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 > /dev/null;;
"pair") pair "$2";;
"unpair")
if [ -z "$2" ]; then
choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'remove which paired device?')
else
if echo "$paired_devices" | grep -q "$2";then
choice="$2"
else
echo "Device $2 not found." > /dev/stderr
exit 1
fi
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 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_device_ids="$( awk '{print "bluetoothctl info "$2}' "$PAIRLIST" | . /dev/stdin | grep -E '(Device |Trusted:)' | sed -e 'N;s/\n/;/;s/^.?*Alias: //' | grep "Trusted: no" | cut -d' ' -f2 )"
untrusted_devices="$( echo "$untrusted_device_ids" | de_alias )"
if [ "$( echo "$untrusted_devices" | wc -l )" -gt 0 ]
then
if [ -z "$2" ]; then
choice=$( echo "$untrusted_devices" | dmenu -l 10 -i -p 'trust which paired device?')
else
if echo "$untrusted_devices" | grep -q "$2"; then
choice="$2"
else
echo "Device $2 not found." > /dev/stderr
exit 1
fi
fi
else
notify-send "bt" "No paired devices that are not trusted already."
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 and check which are connected
#only list those connceted devices as options
# shellcheck disable=SC1091
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" | de_alias )"
if [ -z "$2" ]; then
#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 'disconnect which paired device?')
else
choice="$connected_devices"
fi
else
if echo "$connected_devices" | grep -q "$2"; then
choice="$2"
else
echo "Device $2 not found." > /dev/stderr
exit 1
fi
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") if [ -z "$2" ]; then
choice=$( echo "$paired_devices" | dmenu -l 10 -i -p 'blacklist which paired device from selection?')
else
if echo "$paired_devices" | grep -q "$2"; then
choice="$2"
else
echo "Device $2 not found." > /dev/stderr
exit 1
fi
fi
if [ -n "$choice" ]; then
echo "$choice" >> "$BLACKLIST"
fi;;
*)
echo "choice: $choice"
dev_no=$( echo "$paired_devices" | nl | grep -P "[0-9]+\t$choice$" | awk '{print $1}')
[ "$dev_no" != "" ] && dev_id=$( echo "$connected_ids" | nl | grep -P "^.*$dev_no\t" | awk '{print $2}')
echo "dev_id: $dev_id"
if [ -n "$dev_id" ]; then
power on
if bluetoothctl info "$dev_id" | grep -q "Connected: yes"
then
echo Was connceted... disconnecting
bluetoothctl disconnect "$dev_id"
else
if ! bluetoothctl devices Paired | 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-pairing"
all_devices="$(bluetoothctl devices | cut -d' ' -f2)"
if ! echo "$dev_id" | grep -q "$all_devices"; then
scan on
start_scan="$(cat /tmp/bt_start_scan$$)"
echo "Scan on"
notify-send "bt" "Scanning for new devices until wanted device has been found"
until echo "$dev_id" | grep -q "$all_devices"; do
sleep 1
all_devices="$(bluetoothctl devices | cut -d' ' -f2)"
time_scanned="$(( $(date +'%s') - start_scan ))"
[ "$time_scanned" -gt 30 ] && exit 1
done
fi
echo Attempting to pair...
bluetoothctl pair "$dev_id"
scan off
fi
echo connecting...
bluetoothctl connect "$dev_id"
fi
fi;;
esac
cleanup

15
config Normal file
View File

@ -0,0 +1,15 @@
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 statusbar 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_CONFIG_HOME:-$HOME/.config}/bt/blacklist
PAIRLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/paired
ALIASLIST=${XDG_CONFIG_HOME:-$HOME/.config}/bt/alias
actions="$(printf 'pair\nunpair\n')"
#disconnect menu does not need to be displayed since just directly selecting the connected device will disconnect it
include_disconnect_option=false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB