diff --git a/desktop/btmenu.desktop b/desktop/btmenu.desktop new file mode 100644 index 0000000..92f0dd2 --- /dev/null +++ b/desktop/btmenu.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Categories=Settings;HardwareSettings; +Name=btmenu +GenericName=Bluetooth Menu +Exec=btmenu +Type=Application diff --git a/install.sh b/install.sh index 642d3c7..20128f6 100755 --- a/install.sh +++ b/install.sh @@ -15,7 +15,7 @@ stow -t ~ tridactyl stow -t ~ vim stow -t ~ zsh -# Only create the symlink when the directory doesn't exist +# Create the directory if needed mkdir -p ~/.scripts # Scripts must be in their own directory stow -t ~/.scripts scripts/ @@ -25,3 +25,8 @@ stow -t ~ shell/ # Wallpapers stow -t ~ wallpapers/ + +# Create the directory if needed +mkdir -p ~/.local/share/applications +# Symlink the '.desktop' files (btmenu needs one) +stow -t ~/.local/share/applications desktop diff --git a/scripts/btmenu b/scripts/btmenu new file mode 100755 index 0000000..69da335 --- /dev/null +++ b/scripts/btmenu @@ -0,0 +1,116 @@ +#!/bin/bash + +# Inspired by + +function show_usage() { + echo "A simple bluetoothctl from bluez-utilies interface for dmenu(1)" + echo "" + echo "Usage:" + echo -e "\tbtmenu [OPTION] [DEVICE]" + echo -e "\t\tWithout any options, defaults to connecting to device" + echo "Options:" + echo -e "\t-h, --help\n\t\tshow this message and quit" + echo -e "\t-d, --disconnect\n\t\tDisconnect the device" + exit 0 +} + +function _bluetoothctl() { + LC_ALL=C timeout 30 bluetoothctl +} + +# Connects by default +mode=connect + +# Name -> MAC +declare -A DEVICES +while read -r _ mac name; do + DEVICES["$name"]=$mac +done < <(echo "devices" | _bluetoothctl | awk '$1 == "Device"') + +# Notify the user about the operation +function notify() { + local msg + local is_error + + msg=${1?BUG: no message} + is_error=${2:-0} + + if command -v notify-send >/dev/null 2>&1; then + notify-send "btmenu" "$msg" # TODO: escaping + fi + + if (( is_error )); then + printf 'ERROR: %s\n' "$msg" >&2 + else + printf '%s\n' "$msg" + fi +} + +function execute_mode() { + local mode + local name + local mac + local preposition + local expected_to_connect + local retries + + mode=${1?BUG: missing mode} + retries=15 + + case $mode in + connect) + preposition=to + expected_to_connect=yes + ;; + disconnect) + preposition=from + expected_to_connect=no + ;; + esac + + if ! (( ${#DEVICES[@]} )); then + notify "No devices found. Are they registered with D-Bus?" 1 + return 2 + fi + + name=$(printf '%s\n' "${!DEVICES[@]}" | dmenu -l 10 -p "btmenu" "${dmenu_args[@]}") + [[ $name ]] || return + mac=${DEVICES["$name"]} + + notify "Attempting to $mode $preposition $name" + + while (( retries-- )); do + printf 'power on\n%s %s\n' "$mode" "$mac" | _bluetoothctl + if printf 'info %s\n' "$mac" | + _bluetoothctl | + grep -Pq '^[\t ]+Connected: '"$expected_to_connect"; then + notify "${mode^}ed $preposition $name" + return 0 + fi + sleep 1 + done + + ret="$?" + notify "Failed to $mode $preposition $name" 1 + return "$ret" +} + +i=2 + +# FIXME: I want to have a list of devices with their connection status instead +# of having to launch the script with the correct argument to connect/disconnect +for arg do + if [[ $arg == -- ]]; then + dmenu_args=( "${@:$i}" ) + break + fi + + case "$arg" in + -d|--disconnect) mode=disconnect ;; + -h|--help) show_usage ;; + esac + + (( i++ )) +done + +execute_mode "$mode"