Skip to content

Commit

Permalink
Added ability to view the battery charge of the connected bluetooth d…
Browse files Browse the repository at this point in the history
…evice.

* Updated new Bluetooth battery info functionality.
* Added unit test for new Bluetooth battery info functionality.
* Updated README.
* Removed trailing whitespace from files.
  • Loading branch information
SfyMantissa authored and marioortizmanero committed May 18, 2024
1 parent 4e5995e commit a1a45fe
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 6 deletions.
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
}

0 comments on commit a1a45fe

Please sign in to comment.