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