Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to view the battery charge of connected Bluetooth devices #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,25 @@ Options:
can specify what timeout to use to control the responsiveness, in
seconds.
Default: "0.05"
--icons-bluetooth-battery <icon>[,<icon>...]
Icons for battery level of connected bluetooth headphones.
If no icons are provided, the feature is disabled.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: none
--hide-bluetooth-battery-level
Hide the integer battery level representation.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: none
--battery-max <int>
Set the maximum device battery level.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: \"$BAT_MAX\"
Actions:
help display this message and exit
Expand Down
77 changes: 73 additions & 4 deletions pulseaudio-control
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ OSD="no"
NODE_NICKNAMES_PROP=
VOLUME_STEP=2
VOLUME_MAX=130
BAT_MAX=100
LISTEN_TIMEOUT=0.05
# shellcheck disable=SC2016
FORMAT='$VOL_ICON ${VOL_LEVEL}% $ICON_NODE $NODE_NICKNAME'
declare -A NODE_NICKNAMES
declare -a ICONS_VOLUME
declare -a ICONS_BLUETOOTH_BATTERY
declare -a NODE_BLACKLIST

# Special variable: within the script, pactl, grep, and awk commands are used
Expand All @@ -46,6 +48,8 @@ SINK_OR_SOURCE="ink"
# Environment & global constants for the script
export LC_ALL=C # Some calls depend on English outputs of pactl
END_COLOR="%{F-}" # For Polybar colors
BLUETOOTH_CONFIG="/etc/bluetooth/main.conf" # For Bluetooth configuration file
BLUETOOTH_EXPERIMENTAL_REGEX="^Experimental = true" # Regex to tell whether battery level can be fetched


# Saves the currently default node into a variable named `curNode`. It will
Expand Down Expand Up @@ -73,6 +77,22 @@ function getNodeName() {
}


# Saves the current battery level of a connected Bluetooth device for the node
# passed by parameter into a variable named `BAT_LEVEL`.
# If a node is not a Bluetooth device, `BAT_LEVEL` would be an empty string.
# Requires bluez experimental features to be enabled.
# For details, see: https://wiki.archlinux.org/title/Bluetooth_headset
function getCurCharge() {
local bluetoothIsExperimental=$(grep -Eo "$BLUETOOTH_EXPERIMENTAL_REGEX" "$BLUETOOTH_CONFIG")
if [ "$bluetoothIsExperimental" ]; then
getNodeName "$1"

local bluetoothDeviceMac=$(echo "$nodeName" | sed -e 's/^[a-z_]*\.//' -e 's/\..*$//' | tr '_' ':')
BAT_LEVEL=$(bluetoothctl info "$bluetoothDeviceMac" | grep Battery | sed -E 's/.*\((.*)\)/\1/')
fi
}


# Saves the name to be displayed for the node passed by parameter into a
# variable called `NODE_NICKNAME`.
# If a mapping for the node name exists, that is used. Otherwise, the string
Expand Down Expand Up @@ -195,9 +215,9 @@ function volSync() {
echo "PulseAudio not running"
return 1
fi

getCurVol "$curNode"

if [[ "$NODE_TYPE" = "output" ]]; then
getSinkInputs "$curNode"

Expand Down Expand Up @@ -382,19 +402,38 @@ function output() {
fi

getNickname "$curNode"
getCurCharge "$curNode"

local iconsLen=${#ICONS_BLUETOOTH_BATTERY[@]}
if [ "$iconsLen" -ne 0 ] && [ "$BAT_LEVEL" != "" ]; then
local batSplit=$((BAT_MAX / iconsLen))
for i in $(seq 1 "$iconsLen"); do
if [ $((i * batSplit)) -ge "$BAT_LEVEL" ]; then
BAT_ICON="${ICONS_BLUETOOTH_BATTERY[$((i-1))]}"
break
fi
done

BLUETOOTH_FORMAT='$BAT_ICON ${BAT_LEVEL}%'
if [ "$HIDE_BAT" = "yes" ] && [ "$BAT_LEVEL" != '' ]; then
BLUETOOTH_FORMAT='$BAT_ICON'
fi
else
BLUETOOTH_FORMAT=''
fi

# Showing the formatted message
if [ "$IS_MUTED" = "yes" ]; then
# shellcheck disable=SC2034
VOL_ICON=$ICON_MUTED
content="$(eval echo "$FORMAT")"
content="$(eval echo "$FORMAT" "$BLUETOOTH_FORMAT")"
if [ -n "$COLOR_MUTED" ]; then
echo "${COLOR_MUTED}${content}${END_COLOR}"
else
echo "$content"
fi
else
eval echo "$FORMAT"
eval echo "$FORMAT" "$BLUETOOTH_FORMAT"
fi
}

Expand Down Expand Up @@ -484,6 +523,25 @@ Options:
can specify what timeout to use to control the responsiveness, in
seconds.
Default: \"$LISTEN_TIMEOUT\"
--icons-bluetooth-battery <icon>[,<icon>...]
Icons for battery level of connected bluetooth headphones.
If no icons are provided, the feature is disabled.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: none
--hide-bluetooth-battery-level
Hide the integer battery level representation.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: none
--battery-max <int>
Set the maximum device battery level.
Requires bluez experimental features to be enabled.
For details, see:
https://wiki.archlinux.org/title/Bluetooth_headset
Default: \"$BAT_MAX\"
Actions:
help display this message and exit
Expand Down Expand Up @@ -551,6 +609,17 @@ while [[ "$1" = --* ]]; do
# shellcheck disable=SC2034
ICON_NODE="$val"
;;
--hide-bluetooth-battery-level)
HIDE_BAT=yes
;;
--icons-bluetooth-battery)
if getOptVal "$@"; then shift; fi
IFS=, read -r -a ICONS_BLUETOOTH_BATTERY <<< "${val//[[:space:]]/}"
;;
--battery-max)
if getOptVal "$@"; then shift; fi
BAT_MAX="$val"
;;
--icons-volume)
if getOptVal "$@"; then shift; fi
IFS=, read -r -a ICONS_VOLUME <<< "${val//[[:space:]]/}"
Expand Down
24 changes: 22 additions & 2 deletions tests.bats
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bats
# vim: filetype=sh
#
#
# Polybar PulseAudio Control - tests.bats
#
#
# Simple test script to make sure the most basic functions in this script
# always work as intended. The tests will modify the system's PulseAudio
# setup until it's restarted, so either do that after running the test, or
Expand Down Expand Up @@ -31,6 +31,10 @@ function setup() {
restartPulseaudio
echo "Loading script"
source pulseaudio-control.bash output &>/dev/null

# In case there's a Bluetooth device connected, 3 seconds are well enough
# to reestalish the connection after restarting PulseAudio.
sleep 3
}


Expand Down Expand Up @@ -194,3 +198,19 @@ function setup() {
getNickname "$((10 + offset))" # beyond what exists
[ "$NODE_NICKNAME" = "Sink #$((10 + offset))" ]
}


@test "getCurCharge()" {
getCurNode
getCurCharge "$curNode"

local bluetoothIsExperimental=$(grep -Eo "$BLUETOOTH_EXPERIMENTAL_REGEX" "$BLUETOOTH_CONFIG")
local bluetoothDeviceMac=$(echo "$nodeName" | sed -e 's/^[a-z_]*\.//' -e 's/\..*$//' | tr '_' ':')

if [ "$bluetoothIsExperimental" ] && [ "$bluetoothDeviceMac" ]; then
local expectedBatLevel=$(bluetoothctl info "$bluetoothDeviceMac" | grep Battery | sed -E 's/.*\((.*)\)/\1/')
[ "$BAT_LEVEL" == "$expectedBatLevel" ]
else
[ "$BAT_LEVEL" == '' ]
fi
}
Loading