added new module 'pactl'
* added new module 'pactl' which displays the current default sink and allows to select a different default sink from the popup menu.
This commit is contained in:
parent
58bbcf2ac5
commit
950931e1b9
1 changed files with 142 additions and 0 deletions
142
bumblebee_status/modules/contrib/pactl.py
Normal file
142
bumblebee_status/modules/contrib/pactl.py
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
# pylint: disable=C0111,R0903
|
||||||
|
|
||||||
|
""" Displays the current default sink.
|
||||||
|
|
||||||
|
Left click opens a popup menu that lists all available sinks and allows to change the default sink.
|
||||||
|
|
||||||
|
Per default, this module uses the sink names returned by "pactl list sinks short"
|
||||||
|
|
||||||
|
sample output of "pactl list sinks short":
|
||||||
|
|
||||||
|
2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
|
||||||
|
3 alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDE
|
||||||
|
|
||||||
|
As "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" is not a particularly nice name, its possible to map the name to more a
|
||||||
|
user friendly name. e.g to map "alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" to the name "Headset", add the following
|
||||||
|
bumblebee-status config entry: pactl.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=Headset
|
||||||
|
|
||||||
|
The module also allows to specify individual (unicode) icons for all the sinks. e.g in order to use the icon 🎧 for the
|
||||||
|
"alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo" sink, add the following bumblebee-status config entry:
|
||||||
|
pactl.icon.alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo=🎧
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
* pulseaudio
|
||||||
|
* pactl
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import functools
|
||||||
|
|
||||||
|
import core.module
|
||||||
|
import core.widget
|
||||||
|
import core.input
|
||||||
|
|
||||||
|
import util.cli
|
||||||
|
import util.popup
|
||||||
|
|
||||||
|
|
||||||
|
class Sink(object):
|
||||||
|
def __init__(self, id, internal_name, friendly_name, icon):
|
||||||
|
super().__init__()
|
||||||
|
self.__id = id
|
||||||
|
self.__internal_name = internal_name
|
||||||
|
self.__friendly_name = friendly_name
|
||||||
|
self.__icon = icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self.__id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def internal_name(self):
|
||||||
|
return self.__internal_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def friendly_name(self):
|
||||||
|
return self.__friendly_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
return self.__icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_name(self):
|
||||||
|
display_name = (
|
||||||
|
self.__icon + " " + self.__friendly_name
|
||||||
|
if self.__icon != ""
|
||||||
|
else self.__friendly_name
|
||||||
|
)
|
||||||
|
return display_name
|
||||||
|
|
||||||
|
|
||||||
|
class Module(core.module.Module):
|
||||||
|
def __init__(self, config, theme):
|
||||||
|
super().__init__(config, theme, core.widget.Widget(self.default_sink))
|
||||||
|
|
||||||
|
self.__default_sink = None
|
||||||
|
|
||||||
|
res = util.cli.execute("pactl list sinks short")
|
||||||
|
lines = res.splitlines()
|
||||||
|
|
||||||
|
self.__sinks = []
|
||||||
|
for line in lines:
|
||||||
|
info = line.split("\t")
|
||||||
|
try:
|
||||||
|
friendly_name = self.parameter(info[1], info[1])
|
||||||
|
icon = self.parameter("icon." + info[1], "")
|
||||||
|
self.__sinks.append(Sink(info[0], info[1], friendly_name, icon))
|
||||||
|
except:
|
||||||
|
logging.exception("Couldn't parse sink")
|
||||||
|
pass
|
||||||
|
|
||||||
|
core.input.register(self, button=core.input.LEFT_MOUSE, cmd=self.popup)
|
||||||
|
|
||||||
|
def __sink(self, internal_sink_name):
|
||||||
|
for sink in self.__sinks:
|
||||||
|
logging.info(sink.internal_name)
|
||||||
|
if internal_sink_name == sink.internal_name:
|
||||||
|
return sink
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
try:
|
||||||
|
res = util.cli.execute("pactl info")
|
||||||
|
lines = res.splitlines()
|
||||||
|
self.__default_sink = None
|
||||||
|
for line in lines:
|
||||||
|
if not line.startswith("Default Sink:"):
|
||||||
|
continue
|
||||||
|
internal_sink_name = line.replace("Default Sink: ", "")
|
||||||
|
self.__default_sink = self.__sink(internal_sink_name)
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Could not get pactl info")
|
||||||
|
self.__default_sink = None
|
||||||
|
|
||||||
|
def default_sink(self, widget):
|
||||||
|
if self.__default_sink is None:
|
||||||
|
return "unknown"
|
||||||
|
return self.__default_sink.display_name
|
||||||
|
|
||||||
|
def __on_sink_selected(self, sink):
|
||||||
|
try:
|
||||||
|
util.cli.execute("pactl set-default-sink {}".format(sink.id))
|
||||||
|
self.__default_sink = sink
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception("Couldn't set default sink")
|
||||||
|
|
||||||
|
def popup(self, widget):
|
||||||
|
menu = util.popup.menu()
|
||||||
|
|
||||||
|
for sink in self.__sinks:
|
||||||
|
menu.add_menuitem(
|
||||||
|
sink.friendly_name,
|
||||||
|
callback=functools.partial(self.__on_sink_selected, sink),
|
||||||
|
)
|
||||||
|
menu.show(widget)
|
||||||
|
|
||||||
|
def state(self, widget):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
Loading…
Reference in a new issue