Exemple #1
0
    def __init__(self):
        QtCore.QThread.__init__(self)

        self.pulse = Pulse('volume-mixer-monitor')

        self.__sinksInput = {}
        self.__sinksOutput = {}
Exemple #2
0
    def __init__(self, cm):
        self.pulse = Pulse()
        self.cm = cm

        self.pulse.event_mask_set('all')
        self.pulse.event_callback_set(self.state_change)
        self.pulse.event_listen(timeout=10)
Exemple #3
0
def volume():
    pulse = Pulse()
    for sink in pulse.sink_list():
        if sink.description.lower().startswith('built-in'):
            volume = pulse.volume_get_all_chans(sink)
            return f"{100 * volume:.2f}"
    return 'n/a'
Exemple #4
0
    def __init__(self,args):

        # get arguments
        if 'allow_reload' in args:
            self.allow_reload = args.allow_reload
        else: self.allow_reload = False
        self.restart = args.internal_restart
        self.num_channels = args.num_channels
        self.pulse_mon = Pulse('pa2jack-monitor') 
        self.pulse_act = Pulse('pa2jack-actor')

        # Get jack sink
        self.jack_sink = self._get_jack_sink()

        # Reload jack module with correct channels
        self.reload_jack_module(self.num_channels)

        # Unload module-remap-sink
        for m in self.pulse_act.module_list():
            if m.name == "module-remap-sink":
                self.pulse_act.module_unload(m.index) 

        # Make our stereo remapping sinks
        self.remap_sink_modules = set([self._new_remap_sink("remap_sink_{}".format(i),start=i*2) for i in range(math.floor(self.num_channels/2))])

        # Get sink indices
        self.remap_sinks = [s.index for s in self.pulse_act.sink_list() if s.owner_module in self.remap_sink_modules]

        # Listen to sink_input events and set handler
        self.pulse_mon.event_mask_set('sink_input')
        self.pulse_mon.event_callback_set(self._pa_event_handler)
Exemple #5
0
def switch_pulse_default(prop):
    pulse = Pulse('qtile')
    current_default = getattr(pulse.server_info(),
                              'default_{}_name'.format(prop))

    set_next = False
    things = getattr(pulse, '{}_list'.format(prop))()
    for thing in itertools.chain(things, things):
        if set_next:
            notify("Sound", "Default {} to {}".format(prop, thing.name))
            pulse.default_set(thing)
            break
        elif thing.name == current_default:
            set_next = True

    if prop == 'sink':
        list_cmd = 'list-sink-inputs'
        move_cmd = 'move-sink-input'
    else:
        list_cmd = 'list-source-outputs'
        move_cmd = 'move-source-output'

    streams = subprocess.getoutput(
        "pacmd {} | grep index | awk '{{ print $2 }}'".format(list_cmd))
    streams = [int(idx.strip()) for idx in streams.splitlines()]

    for stream in streams:
        cmd = "pacmd {cmd} {stream_id} {thing_id}".format(cmd=move_cmd,
                                                          stream_id=stream,
                                                          thing_id=thing.index)
        print(cmd)
        subprocess.check_call(shlex.split(cmd))
Exemple #6
0
 def __init__(self):
     self.pulse = Pulse('volume-controller')
     self.sinks = self.pulse.sink_list()
     self.seeed_sink = None
     for i in self.sinks:
         if "seeed" in i.name:
             self.seeed_sink = i
             break
Exemple #7
0
    def async_listener(self):
        self.pulse_d = Pulse('volume-daemon')
        self.pulse_d.event_mask_set('sink')

        # Glib.idle_add is to run the callback in the UI thread
        self.pulse_d.event_callback_set(
            lambda e: GLib.idle_add(self.callback)
        )
        self.pulse_d.event_listen()
Exemple #8
0
    def async_listener(self):
        self.pulse_d = Pulse('volume-daemon')
        self.pulse_d.event_mask_set('sink', 'card')

        # Glib.idle_add is to run the callback in the UI thread
        self.pulse_d.event_callback_set(self.callback())
        try:
            self.pulse_d.event_listen()
        except PulseDisconnected:
            time.sleep(3)
            self.pulse = Pulse('volume-control')
            self.async_listener()
Exemple #9
0
    def __init__(self):
        self.pulse = Pulse('volume-mixer')

        self.SinksMonitoringThread = SinksMonitoring()

        self.addSinkInput = self.SinksMonitoringThread.addSinkInput
        self.removeSinkInput = self.SinksMonitoringThread.removeSinkInput
        self.changeVolumeInput = self.SinksMonitoringThread.changeVolumeInput

        self.addSinkOutput = self.SinksMonitoringThread.addSinkOutput
        self.removeSinkOutput = self.SinksMonitoringThread.removeSinkOutput
        self.changeVolumeOutput = self.SinksMonitoringThread.changeVolumeOutput

        self.SinksMonitoringThread.start()
Exemple #10
0
class Volume(StatusItem):
    def __init__(self, **kwargs):
        super().__init__()
        self.pulse = Pulse("deity i3bar statusitem")
        self.muted = True
        self.volume = -1

    def refresh(self, periodic):
        if not periodic:
            sink = self.pulse.get_sink_by_name("@DEFAULT_SINK@")
            muted = bool(sink.mute)
            volume = int(round(sink.volume.value_flat * 100))
            has_changed = muted != self.muted or volume != self.volume
            self.muted = muted
            self.volume = volume
            return has_changed
        return False

    def color(self):
        if self.muted:
            return Color.NEUTRAL
        return Color.POSITIVE

    def full_text(self):
        return "\uf028 " + str(self.volume) + "%"
def setup_platform(hass, config, add_entities, discovery_info=None):
    """Read in all of our configuration, and initialize the loopback switch."""
    name = config.get(CONF_NAME)
    sink_name = config.get(CONF_SINK_NAME)
    source_name = config.get(CONF_SOURCE_NAME)
    host = config.get(CONF_HOST)
    port = config.get(CONF_PORT)

    hass.data.setdefault(DOMAIN, {})

    server_id = str.format("{0}:{1}", host, port)

    if host:
        connect_to_server = server_id
    else:
        connect_to_server = None

    if server_id in hass.data[DOMAIN]:
        server = hass.data[DOMAIN][server_id]
    else:
        server = Pulse(server=connect_to_server,
                       connect=False,
                       threading_lock=True)
        hass.data[DOMAIN][server_id] = server

    add_entities([PALoopbackSwitch(name, server, sink_name, source_name)],
                 True)
    def __init__(self,
                 sink_name: Optional[str] = None,
                 volume_boost: bool = False):
        """

        :param sink_name:  Sink name to use. Empty uses default sink
        :param volume_boost: Whether to allow setting volume above 1.0 - uses software boost
        """
        self._sink_name = sink_name
        self._sink_info: Optional[PulseSinkInfo]
        self._volume_boost = volume_boost
        self._pulse_connector = Pulse('py3status-pulse-connector',
                                      threading_lock=True)
        self._pulse_connector_lock = threading.Lock()
        self._volume: Optional[Volume] = None
        self._backend_thread = threading.Thread
Exemple #13
0
    def __init__(self, volctl):
        self._volctl = volctl
        self._pulse_loop_paused = False
        self._pulse = Pulse(client_name=PROGRAM_NAME, connect=False)

        self._poller_thread = None
        self._pulse_lock, self._pulse_hold = threading.Lock(), threading.Lock()
        signal.signal(signal.SIGALRM, self._handle_pulse_events)

        # Stream monitoring
        self._monitor_streams = {}
        self._read_cb_ctypes = c.PA_STREAM_REQUEST_CB_T(self._read_cb)
        self._samplespec = c.PA_SAMPLE_SPEC(format=c.PA_SAMPLE_FLOAT32BE,
                                            rate=25,
                                            channels=1)

        self._connect()
Exemple #14
0
    def get_volume(self):
        from pulsectl import Pulse
        with Pulse() as pulse:
            for input in pulse.sink_input_list():
                if input.name == 'Spotify':
                    self.spotify = input
                    break

        self.volume = self.spotify.volume.value_flat
Exemple #15
0
    def __init__(self):
        self.pulse = Pulse("g15mediadaemon-spotifyapp")
        self.lastpid = -1
        self.proxy = None
        self.player = None
        self.props = None

        if self.is_running():
            pass
Exemple #16
0
class pulseManagement():
    def __init__(self):
        self.pulse = Pulse('volume-mixer')

        self.SinksMonitoringThread = SinksMonitoring()

        self.addSinkInput = self.SinksMonitoringThread.addSinkInput
        self.removeSinkInput = self.SinksMonitoringThread.removeSinkInput
        self.changeVolumeInput = self.SinksMonitoringThread.changeVolumeInput

        self.addSinkOutput = self.SinksMonitoringThread.addSinkOutput
        self.removeSinkOutput = self.SinksMonitoringThread.removeSinkOutput
        self.changeVolumeOutput = self.SinksMonitoringThread.changeVolumeOutput

        self.SinksMonitoringThread.start()

    def setVolSink(self, sink, vol):
        self.pulse.volume_set(
            sink, PulseVolumeInfo(vol / 100, len(sink.volume.values)))
Exemple #17
0
 def __init__(self, config, bt_device, sp_device, hs_device):
     """
     Constructor for PulseAudio connection.
     :param config: 
     """
     self.config = config
     self.pulse_conn = PulseLib('maxime-manage_connection')
     self.ladspa_device = self._lookup_sink_input_device("LADSPA Stream")
     self.bt_device = bt_device
     self.hs_device = hs_device
     self.sp_device = sp_device
Exemple #18
0
    def _toggle_mute_sinks(self, sinks):
        """
        Simply toggle the mute status of all given sinks.
        :param sinks: A list of sink objects.
        :return: Nothing.
        """

        # Toggle the mute status
        with Pulse(threading_lock=True) as pulse:
            for sink in sinks:
                muted = bool(sink.mute)
                pulse.mute(sink, mute=not muted)
Exemple #19
0
    def __init__(self, i3, bat, bus, bar, display_title=True):
        self.out = ''
        self.datetime = ''
        self.workspaces = dict()
        self.volume = ''
        self.battery = ''
        self.i3 = i3
        self.bar = bar
        self.display_title = display_title
        focused = i3.get_tree().find_focused()
        self.focusedWinTitle = focused.name if not focused.type == 'workspace' and display_title else ''
        self.currentBindingMode = ''
        self.mode = ''
        self.bat = bat
        self.bus = bus
        self.pulse = Pulse('my-client-name')
        self.outputs = ''
        self.notitle = " %%{R B-}%s%%{F-}" % self.bar.sep_left
        self.title = self.notitle

        self.set_outputs()
class RecordingIndicator(tk.Tk):
    def __init__(self):
        super().__init__()

        # Remove root window to avoid two windows
        # See : https://stackoverflow.com/a/48289343/1079254
        self.withdraw()

        # Register overlay
        self.overlay = Overlay(self,
                               cursor='hand1',
                               borderwidth=2,
                               relief='solid')

        # Init pulse (do not support multi-thread !)
        self.pulse = Pulse(self.__class__.__name__)

        self.recording_color = [
            '#c4181a',
            '#f9df74',
            '#ea2b1f',
        ]

    # Custom methods
    def active_recording(self):
        self.overlay.icon.config(bg=random.choice(self.recording_color))

    def inactive_recording(self):
        self.overlay.icon.config(bg='grey')

    def do_update(self):
        # Check status for recording indicator
        if self.is_sources_muted():
            self.inactive_recording()
        else:
            self.active_recording()

        # Update tkinter window
        self.update_idletasks()
        self.update()

    def is_sources_muted(self):
        """Return False if at least one source is recording
        """
        for source in self.pulse.source_list():
            # Exclude monitors input
            if source.name.endswith('.monitor'):
                continue
            # mute = 0 mean recording
            if source.mute == 0:
                return False
        # No source recording found, all are muted
        return True
    def __init__(self):
        super().__init__()

        # Remove root window to avoid two windows
        # See : https://stackoverflow.com/a/48289343/1079254
        self.withdraw()

        # Register overlay
        self.overlay = Overlay(self,
                               cursor='hand1',
                               borderwidth=2,
                               relief='solid')

        # Init pulse (do not support multi-thread !)
        self.pulse = Pulse(self.__class__.__name__)

        self.recording_color = [
            '#c4181a',
            '#f9df74',
            '#ea2b1f',
        ]
Exemple #22
0
def run():
    loop = MainLoop()
    bus = SystemBus(mainloop=dglib.DBusGMainLoop())
    dglib.threads_init()

    bat_object = bus.get_object(
        'org.freedesktop.UPower',
        '/org/freedesktop/UPower/devices/battery_BAT1')
    bat = Interface(bat_object, 'org.freedesktop.DBus.Properties')

    lemonbar = Feeder(i3, bat, bus, bar, display_title)
    lemonbar.render_all()

    pulse = Pulse('event-printer')
    pulse.event_mask_set('sink', 'server')
    pulse.event_callback_set(lemonbar.on_volume_event)
    pulse_thread = Thread(target=pulse.event_listen, args=[0])
    pulse_thread.daemon = True
    pulse_thread.start()

    bat_object.connect_to_signal(
        "PropertiesChanged",
        lemonbar.on_battery_event,
        dbus_interface='org.freedesktop.DBus.Properties')
    bat_thread = Thread(target=loop.run)
    bat_thread.daemon = True
    bat_thread.start()

    sched = BackgroundScheduler(daemon=True)
    sched.add_job(lemonbar.on_timedate_event, 'cron', second=0)
    sched.start()

    def shutdown(caller, e):
        bar.stop_bar(wbak, bar_pid)
        print(e.change)
        exit(0)

    def reload_bar(caller, data):
        global bar_pid
        bar.stop_bar(wbak, bar_pid)
        #print("reloading...")
        bar_pid = bar.start_bar()
        lemonbar.set_outputs()
        lemonbar.render_all(caller=caller, e=data)

    i3.on('workspace::urgent', lemonbar.on_workspace_event)
    i3.on('workspace::empty', lemonbar.on_workspace_event)
    if display_title:
        i3.on('window::title', lemonbar.on_window_title_change)
        i3.on('window::close', lemonbar.on_window_close)
        i3.on('window::focus', lemonbar.on_window_title_change)
        i3.on('workspace::focus', lemonbar.on_workspace_focus)
    else:
        i3.on('workspace::focus', lemonbar.on_workspace_event)
    i3.on('mode', lemonbar.on_binding_mode_change)
    i3.on('output', reload_bar)
    i3.on('shutdown', shutdown)
    i3.main()
Exemple #23
0
        def _activate_profile():
            try:
                with Pulse(PULSE_NAME) as pulse:
                    card = pulse.get_sink_by_name(card_name)
                    pulse.card_profile_set(card, profile_name)

            except PulseIndexError:
                _LOGGER.error("Can't find %s profile %s", card_name, profile_name)
                raise PulseAudioError()
            except PulseError as err:
                _LOGGER.error(
                    "Can't activate %s profile %s: %s", card_name, profile_name, err
                )
                raise PulseAudioError()
def list_modules(pulseaudio: pulsectl.Pulse) -> List[pulsectl.PulseModuleInfo]:
    """
    Shortcut for the pactl list modules short command.
    :return: String output from the command.
    """
    listable_module_names = [
        'module-null-sink',
        'module-loopback',
        'module-null-source',
        'module-remap-source',
    ]
    return [
        module for module in pulseaudio.module_list()
        if module.name in listable_module_names
    ]
Exemple #25
0
        def _activate_profile():
            try:
                with Pulse(PULSE_NAME) as pulse:
                    card = pulse.get_card_by_name(card_name)
                    pulse.card_profile_set(card, profile_name)

            except PulseIndexError as err:
                raise PulseAudioError(
                    f"Can't find {card_name} profile {profile_name}",
                    _LOGGER.error) from err
            except PulseError as err:
                raise PulseAudioError(
                    f"Can't activate {card_name} profile {profile_name}: {err}",
                    _LOGGER.error,
                ) from err
Exemple #26
0
    def _change_volume_sinks(self, sinks, rotation):
        """
        Simple change the volume of all given sinks and display a notification.
        :param sinks: A list of sink objects.
        :param rotation: The amount and direction of the rotation.
        :return: Nothing.
        """

        # Change the volume of the sinks
        with Pulse(threading_lock=True) as pulse:
            for sink in sinks:
                pulse.volume_change_all_chans(sink, rotation * 0.005)

                # Show the notification
                self._display_notification(sink)
Exemple #27
0
    def _get_sinks(self):
        """
        Get a list of active pulseaudio sinks
        :return: List. A list containing all the active sink objects.
        """

        # Get the list of input sinks
        with Pulse(threading_lock=True) as pulse:
            sinks = [
                sink for sink in pulse.sink_input_list() if sink.proplist.get(
                    "application.process.id", None) is not None
            ]

        # Return the list of active sinks
        return sinks
Exemple #28
0
        def _set_default():
            try:
                with Pulse(PULSE_NAME) as pulse:
                    if stream_type == StreamType.INPUT:
                        # Get source and set it as default
                        source = pulse.get_source_by_name(name)
                        pulse.source_default_set(source)
                    else:
                        # Get sink and set it as default
                        sink = pulse.get_sink_by_name(name)
                        pulse.sink_default_set(sink)

            except PulseIndexError:
                _LOGGER.error("Can't find %s stream %s", source, name)
                raise PulseAudioError()
            except PulseError as err:
                _LOGGER.error("Can't set %s as stream: %s", name, err)
                raise PulseAudioError()
Exemple #29
0
class Volume(ThreadModule):
    defaults = {
        "icon_low": Tools.sym(""),
        "icon_high": Tools.sym(""),
        "icon_muted": Tools.sym("")
    }

    def __init__(self, sink_index=0, template="$icon $volume", interval=2, **kwargs):
        super().__init__(template, interval, **kwargs)
        self.pulse = Pulse()
        self.sink_index = sink_index

    def clicked(self, button, *_):
        if button == 1:
            os.system("pavucontrol")
        elif button == 4 or button == 5:
            sink = self.pulse.sink_list()[0]
            cur_volume = self.pulse.volume_get_all_chans(sink)
            if button == 4 and cur_volume < 1.0:
                self.pulse.volume_change_all_chans(sink, 0.05)
            elif cur_volume > 0.0:
                self.pulse.volume_change_all_chans(sink, -0.05)
            self._set_values()
            self.trigger_refresh()

    def _set_values(self):
        info = self.pulse.sink_info(self.sink_index)
        volume = int(self.pulse.volume_get_all_chans(info)*100)
        self.values["volume"] = volume
        if info.mute == 1:
            self.values["icon"] = self.values["icon_muted"]
            self.props["color"] = "#ff0000"
        else:
            self.props["color"] = "#ffffff"
            if volume < 30:
                self.values["icon"] = self.values["icon_low"]
            else:
                self.values["icon"] = self.values["icon_high"]

    def repeat(self):
        self._set_values()
Exemple #30
0
        def _set_default():
            source = sink = None
            try:
                with Pulse(PULSE_NAME) as pulse:
                    if stream_type == StreamType.INPUT:
                        # Get source and set it as default
                        source = pulse.get_source_by_name(name)
                        pulse.source_default_set(source)
                    else:
                        # Get sink and set it as default
                        sink = pulse.get_sink_by_name(name)
                        pulse.sink_default_set(sink)

            except PulseIndexError as err:
                raise PulseAudioError(f"Can't find {source} stream {name}",
                                      _LOGGER.error) from err
            except PulseError as err:
                raise PulseAudioError(f"Can't set {name} as stream: {err}",
                                      _LOGGER.error) from err
    def __init__(self, observer):
        """
        Initialize the super class and define the local members
        :param path: The path to the powermate device
        """
        self._long_pressed = False
        self._pulse = Pulse(threading_lock=True)
        self._stored_app = None
        self._display = Display()  # Connects to the default display
        self._note = pynotify.Notification("Volume", "0", "/usr/share/icons/Faenza/apps/48/"
                                                          "gnome-volume-control.png")
        self._note.set_urgency(0)

        self._led = PowermateLed()
        self._led.max()

        self._rofi = Rofi()
        self._rofi.hide_scrollbar = True
        self._rofi.prompt = "App. name?"
Exemple #32
0
def pulse_main(notify, mac):
    notify.update("Init pulse...")
    with Pulse('blue-audio-con') as pulse:
        notify.update("Pulse connected, looking for bluez card and sink")

        card = find_card(pulse, mac)
        notify.update("Resetting profiles for %s" % card)
        reset_card(pulse, card)

        sink = find_sink(mac, pulse)
        notify.update("Setting default sink to %s" % sink)
        pulse.sink_default_set(sink)

        # for input in pulse.sink_input_list():
        #     notify.update("Moving input %s to bluez sink" % input)
        #     pulse.sink_input_move(input.index, sink.index)

        notify.update("Setting GNOME sink to %s" % sink.name)
        set_gnome_sink(sink.name)

        notify.update("Setting profile for %s to a2dp" % card)
        pulse.card_profile_set(card, "a2dp_sink")
Exemple #33
0
class SpotifyApp(G15App):
    def __init__(self):
        self.pulse = Pulse("g15mediadaemon-spotifyapp")
        self.lastpid = -1
        self.proxy = None
        self.player = None
        self.props = None

        if self.is_running():
            pass

    def _get_iface(self):
        try:
            self.proxy = dbus.SessionBus().get_object('org.mpris.MediaPlayer2.spotify', '/org/mpris/MediaPlayer2')
            self.player = dbus.Interface(self.proxy, dbus_interface='org.mpris.MediaPlayer2.Player')
            self.props = dbus.Interface(self.proxy, dbus_interface='org.freedesktop.DBus.Properties')
        except DBusException:
            self.proxy = None
            self.player = None
            self.props = None

        for s in self.pulse.sink_input_list():
            if s.name.lower() == "spotify":
                self.sink = s

        return True

    @property
    def name(self):
        return "spotify"

    def is_running(self):
        s = subprocess.Popen(["ps cxo pid,command --sort=start_time | grep spotify | grep -v \<defunct\>"], shell=True, stdout=subprocess.PIPE)

        for line in s.stdout:
            if self.lastpid != line or self.player is None:
                self.lastpid = line
                self._get_iface()

            return True

        self.lastpid = -1
        self.proxy = None
        self.player = None
        self.props = None
        return False

    def get_metadata(self):
        if self.is_running():
            md = self.props.Get('org.mpris.MediaPlayer2.Player', 'Metadata')
            return {v[6:].lower(): md[v] for v in md}

        return {}

    def tick(self, g15):
        if self.is_running():
            position = self.props.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')
            md = self.get_metadata()

            g15.render_string("%s - %s" % (md['artist'][0], md['title']), 2, g15daemon.G15_TEXT_LARGE, 0, 0, True)
            g15.render_string("%s / %s" % (position, self.formatTime(md['length'])), 3, g15daemon.G15_TEXT_LARGE, 0, 0)
        else:
            g15.render_string("not running", 2, g15daemon.G15_TEXT_LARGE, 0, 0)

    def start(self):
        subprocess.Popen("/usr/bin/spotify", shell=True)

    def play_pause(self):
        if self.is_running():
            self.player.PlayPause()
        else:
            self.start()

    def stop(self):
        if self.is_running():
            self.player.Stop()

    def next(self):
        if self.is_running():
            self.player.Next()

    def previous(self):
        if self.is_running():
            self.player.Previous()

    def volume_down(self):
        if self.is_running():
            nextvol = max(0.0, self.sink.volume.value_flat - 0.1)
            self.pulse.volume_set_all_chans(self.sink, nextvol)

    def volume_up(self):
        if self.is_running():
            nextvol = min(1.0, self.sink.volume.value_flat + 0.1)
            self.pulse.volume_set_all_chans(self.sink, nextvol)
class Dispatcher:
    """
    A simple dispatcher class that receive powermate events and dispatch them to the right controller
    """

    def __init__(self, observer):
        """
        Initialize the super class and define the local members
        :param path: The path to the powermate device
        """
        self._long_pressed = False
        self._pulse = Pulse(threading_lock=True)
        self._stored_app = None
        self._display = Display()  # Connects to the default display
        self._note = pynotify.Notification("Volume", "0", "/usr/share/icons/Faenza/apps/48/"
                                                          "gnome-volume-control.png")
        self._note.set_urgency(0)

        self._led = PowermateLed()
        self._led.max()

        self._rofi = Rofi()
        self._rofi.hide_scrollbar = True
        self._rofi.prompt = "App. name?"

    def short_press(self):
        """
        Manage the short_press event
        :return: None
        """

        # Get the list of active sinks
        sinks = self._get_sinks()
        # Get the names of the apps linked to the sinks
        app_sinks = {"{} {}".format(sink.proplist.get("application.name"), sink.index): sink for sink in sinks}
        if len(app_sinks) > 1:
            # Display a menu to select the application to control
            try:
                res = self._rofi(app_sinks)
            except MenuError:
                return
            app_sink = res.value
        elif len(app_sinks) == 1:
            _, app_sink = app_sinks.popitem()
        else:
            app_sink = None

        # If successful
        if app_sink is not None:
            # Toggle the mute status of the selected sink
            self._toggle_mute_sinks([app_sink])

            # Declare a new notification
            self._note.update("Toggle Mute status", "{}".format(app_sink.proplist.get("application.name")), "/usr/share/icons/Faenza/apps/48/gnome-volume-control.png")

            # Show the notification
            self._note.show()

    def long_press(self):
        """
        For the moment this method simply toggles the lights on/off
        :return: A LedEvent class
        """

        if self._long_pressed:
            # Re-initialize the state of the powermate
            self._long_pressed = False
            self._stored_app = None
            # Just light up the powermate
            self._led.max()
        else:
            # Get the list of active sinks
            sinks = self._get_sinks()
            # Get the names of the apps linked to the sinks
            app_sinks = {sink.proplist.get("application.name"):sink.proplist.get("application.process.binary") for sink in sinks if sink.proplist.get("application.process.binary") not in self._get_active_win_class()}
            if len(app_sinks) > 1:
                # Display a menu to select the application to control
                try:
                    res = self._rofi(app_sinks)
                except MenuError:
                    return
                app_name = res.value
            elif len(app_sinks) == 1:
                _, app_name = app_sinks.popitem()
            else:
                app_name = None

            # If successful
            if app_name is not None:
                # Store the list of sinks corresponding to the app name
                self._stored_app = app_name

                # Toggle the long press state
                self._long_pressed = True

                # Have the powermate pulse
                self._led.pulse()
            else:
                # Make sure the long press flag is off
                self._long_pressed = False
                # Stop the pulse
                self._led.max()

    def rotate(self, rotation):
        """
        Manage the rotate event
        :param rotation: The direction of rotation negative->left, positive->right
        :return: None
        """

        # Get the class of the active window
        win_cls = self._get_active_win_class()
        if win_cls is not None:
            # Change the volume of the sinks
            self._change_volume_sinks(self._get_app_sinks(win_cls), rotation)

    def push_rotate(self, rotation):
        """
        Changes the volume of the sinks registered by the long_press event, according to the given rotation.
        :param rotation: The direction and amplitude of the rotation. (negative = left, positive = right).
        :return: Nothing.
        """

        # Change the volume of the current sinks
        self._change_volume_sinks(self._get_app_sinks(self._stored_app), rotation)

    def _toggle_mute_sinks(self, sinks):
        """
        Simply toggle the mute status of all given sinks.
        :param sinks: A list of sink objects.
        :return: Nothing.
        """

        # Toggle the mute status
        for sink in sinks:
            muted = bool(sink.mute)
            self._pulse.mute(sink, mute=not muted)

    def _change_volume_sinks(self, sinks, rotation):
        """
        Simple change the volume of all given sinks and display a notification.
        :param sinks: A list of sink objects.
        :param rotation: The amount and direction of the rotation.
        :return: Nothing.
        """

        # Change the volume of the sinks
        for sink in sinks:
            self._pulse.volume_change_all_chans(sink, rotation * 0.005)

            # Show the notification
            self._display_notification(sink)


    def _get_active_win_class(self):
        """
        Use the xlib module to get the class of the window that has the focus
        :return: Return the window class or None if none found
        """

        # Get the window that has the focus
        focus_win = self._display.get_input_focus().focus
        # Get the window class
        win_cls = focus_win.get_wm_class()
        if win_cls is None:
            # Get the class of the parent window
            parent_cls = focus_win.query_tree().parent.get_wm_class()
            if parent_cls is not None:
                return str(parent_cls[-1].lower())
            else:
                return None
        else:
            return str(win_cls[-1].lower())

    def _get_app_sinks(self, app_name):
        """
        Get the sinks corresponding to the given application
        :param app_name: Name of the application
        :return: List of sink objects otherwise.
        """

        # Make sure the app_name is a string
        if isinstance(app_name, str) and app_name is not None:
            # Get the list of input sinks
            sinks = self._get_sinks()
            # Return the list of sinks corresponding to the application
            return [sink for sink in sinks if sink.proplist.get("application.process.binary").lower() == app_name]
        else:
            return []

    def _get_sinks(self):
        """
        Get a list of active pulseaudio sinks
        :return: List. A list containing all the active sink objects.
        """

        # Get the list of input sinks
        sinks = [sink for sink in self._pulse.sink_input_list()
                 if sink.proplist.get("application.process.binary", None) is not None]

        # Return the list of active sinks
        return sinks

    def _display_notification(self, sink_in):
        """
        Display a notification showing the overall current volume.
        :param volume: A float representing the value of the current sink input.
        :return: Nothing.
        """

        # Get the volume of the input sink
        volume = self._pulse.volume_get_all_chans(sink_in)

        # Get the main sink
        for sink in self._pulse.sink_list():
            if sink.index == sink_in.sink:
                main_vol = sink.volume.value_flat
                break
        else:
            main_vol = 1

        # Declare a new notification
        self._note.update("Volume", "{:.2%}".format(volume * main_vol), "/usr/share/icons/Faenza/apps/48/"
                                                                        "gnome-volume-control.png")

        # Show the notification
        self._note.show()

    def _handle_exception(self):
        """
        Close the connection to the pulse server.
        :return: Nothing
        """

        # Close the connection to the pulse server
        self._pulse.close()
        self._led.off()
Exemple #35
0
#!/usr/bin/python

from   pulsectl import Pulse

### Sound check
# https://pypi.python.org/pypi/pulsectl
# equivalent to:
#   cat /proc/asound/card*/pcm*/sub*/status | grep RUNNING
#   pacmd list-sink-inputs | wc -l
try:
  pulse = Pulse('sleep-if-idle')
  if len(pulse.sink_input_list()):
    active = 1
  else:
    active = 0
except:
  active = 0
print(active, end='')