# pylint: disable=C0111,R0903 """Displays volume and mute status of PulseAudio devices. Parameters: * pasink.controlApp : What program to run on right click (defaults to pavucontrol) Aliases: pasink, pasource Requires the following executable: * pactl """ import re import bumblebee.util import bumblebee.input import bumblebee.output import bumblebee.engine ALIASES = [ "pasink", "pasource" ] class Module(bumblebee.engine.Module): def __init__(self, engine, config): super(Module, self).__init__(engine, config, bumblebee.output.Widget(full_text=self.volume) ) self._left = 0 self._right = 0 self._mono = 0 self._mute = False channel = "sink" if self.name == "pasink" else "source" self._patterns = [ { "expr": "Name:", "callback": (lambda line: False) }, { "expr": "Mute:", "callback": (lambda line: self.mute(False if " no" in line.lower() else True)) }, { "expr": "Volume:", "callback": self.getvolume }, ] engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd=self.parameter("controlApp", "pavucontrol")) events = [ { "type": "mute", "action": "toggle", "button": bumblebee.input.LEFT_MOUSE }, { "type": "volume", "action": "+2%", "button": bumblebee.input.WHEEL_UP }, { "type": "volume", "action": "-2%", "button": bumblebee.input.WHEEL_DOWN }, ] for event in events: engine.input.register_callback(self, button=event["button"], cmd="pactl set-{}-{} @DEFAULT_{}@ {}".format(channel, event["type"], channel.upper(), event["action"])) def mute(self, value): self._mute = value def getvolume(self, line): if "mono" in line: m = re.search(r'mono:.*\s*\/\s*(\d+)%', line) if m: self._mono = m.group(1) else: m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line) if m: self._left = m.group(1) self._right = m.group(2) return True def _default_device(self): output = bumblebee.util.execute("pactl info") pattern = "Default Sink: " if self.name == "pasink" else "Default Source: " for line in output.split("\n"): if line.startswith(pattern): return line.replace(pattern, "") return "n/a" def volume(self, widget): if int(self._mono) > 0: return "{}%".format(self._mono) elif self._left == self._right: return "{}%".format(self._left) else: return "{}%/{}%".format(self._left, self._right) return "n/a" def update(self, widgets): channel = "sinks" if self.name == "pasink" else "sources" device = self._default_device() result = bumblebee.util.execute("pactl list {}".format(channel)) found = False for line in result.split("\n"): if device in line: found = True continue if found == False: continue for pattern in self._patterns: if not pattern["expr"] in line: continue if pattern["callback"](line) == False and found == True: return def state(self, widget): if self._mute: return [ "warning", "muted" ] return [ "unmuted" ] # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4