Saturday, February 14, 2015

Sound volume change notifications using pynotify

As I started to use the i3 window manager I wanted to have a visual feedback on sound volume change events. Usually it's done by a daemon that binds to specific keyboard combinations to adjust volume and to send notifications to a notify daemon. In xfce this is done by the xfce4-volumed. I should also point here to other lightweight volume daemons like volnoti or pa-applet. Without a dedicated daemon it is also pretty much simple to handle sound volume keys by a simple bash script which is bind to specific keyboard combinations.

I've seen several examples of such a script the only problem with them was that notify-send command used to send notifications still does not return an id of a notification shown which results in several notification popups floating at the same time. As it's described here one could patch notify-send command so that it will return an id if asked but I came up with an easier solution using python-notify lib. Until patched notify-send is available in an official repository one could use this pynotify workaround. Here is an example of pynotify usage in a script to adjust sound volume:
$ cat bin/volume_notify.sh
#!/bin/bash

# $0 - icon_name, $2 - volume, $3 - id
print_cmd()
{
cat <<HEREDOC
import pynotify
pynotify.init("volume_notif.py")
notif=pynotify.Notification("Volume", "", "$1")
notif.set_hint_int32("value",$2)
notif.set_property("id", $3)
notif.show()
print notif.get_property("id")
HEREDOC
}

step=5
id_file="/tmp/notify_volume.id"

icon_high="/usr/share/icons/elementary-xfce/notifications/48/audio-volume-high.png"
icon_low="/usr/share/icons/elementary-xfce/notifications/48/audio-volume-low.png"
icon_medium="/usr/share/icons/elementary-xfce/notifications/48/audio-volume-medium.png"
icon_muted="/usr/share/icons/elementary-xfce/notifications/48/audio-volume-muted.png"
icon_off="/usr/share/icons/elementary-xfce/notifications/48/audio-volume-off.png"

case $1 in 
  up)
      amixer -q set Master $step+ ;;
  down)
      amixer -q set Master $step- ;;
  toggle)
      #pactl list sinks|grep -q Mute:.yes;pactl set-sink-mute 0 ${PIPESTATUS[1]} ;;
      # amixer needs -D pulse switch as a workaround for the pulseaudio
      # check http://goo.gl/4z4U6R
      amixer -q -D pulse set Master toggle ;;
  *)
      echo "Usage: $0 up/down/toggle"
      exit 1 ;;
esac

muted=$(amixer get Master | tail -n1 | sed -nr 's/.*\[([a-z]+)\].*/\1/p')
volume=$(amixer get Master | tail -n1 | sed -nr 's/[^\[]*.([^%]*).*/\1/p')
if ((volume==0)); then
  icon=$icon_off
elif ((volume<34)); then
  icon=$icon_low
elif ((volume<67)); then
  icon=$icon_medium
else
  icon=$icon_high
fi
if [[ $muted == "off" ]]; then
  icon=$icon_muted
fi

id=$(cat $id_file 2>/dev/null);id=${id:-0}
idn=$(python -c "$(print_cmd $icon $volume $id)")
(($idn != $id)) && echo "$idn" > "$id_file"
exit 0

Here pynotify module is used to send a notification and return its id. This id is stored in a temp file defined by id_file variable so that it can be accessed by another script process.

ps: lines 42-43 could be optimized to this:
val=$(amixer get Master | tail -n1 | sed -nr 'h;s/[^\[]*.([^%]*).*/\1/p;g;s/.*\[([a-z]+)\].*/\1/p')
muted=${val#*$'\n'}
volume=${val%$'\n'*}
This reduces number of created subprocesses two times

1 comment:

Felipe Marinho said...

Thank you!!
Here is a modded version that worked for me..

$ cat volume.sh
#!/bin/bash

# $0 - icon_name, $2 - volume, $3 - id
print_cmd()
{
cat </dev/null);id=${id:-0}
idn=$(python2 -c "$(print_cmd $icon $volume $id)")
(($idn != $id)) && echo "$idn" > "$id_file"
exit 0