Add script for swapping audio outs
This commit is contained in:
@@ -40,7 +40,7 @@
|
|||||||
"margin-right": 16,
|
"margin-right": 16,
|
||||||
"modules-left": ["gamemode", "battery", "temperature", "cpu", "memory", "network"],
|
"modules-left": ["gamemode", "battery", "temperature", "cpu", "memory", "network"],
|
||||||
"modules-center": [],
|
"modules-center": [],
|
||||||
"modules-right": ["mpris", "pulseaudio", "backlight", "idle_inhibitor", "clock"],
|
"modules-right": ["mpris", "pulseaudio", "custom/output-device", "backlight", "idle_inhibitor", "clock"],
|
||||||
"clock": {
|
"clock": {
|
||||||
"format": "{:%a %b %d %I:%M %p}",
|
"format": "{:%a %b %d %I:%M %p}",
|
||||||
"tooltip": false
|
"tooltip": false
|
||||||
@@ -143,5 +143,14 @@
|
|||||||
"format": "",
|
"format": "",
|
||||||
"tooltip-format": "An rpm-ostree deployment is pending and will be applied upon the next reboot",
|
"tooltip-format": "An rpm-ostree deployment is pending and will be applied upon the next reboot",
|
||||||
"exec": "rpm-ostree status --json | jq -e '.deployments[0].staged'"
|
"exec": "rpm-ostree status --json | jq -e '.deployments[0].staged'"
|
||||||
|
},
|
||||||
|
"custom/output-device": {
|
||||||
|
"interval": 60,
|
||||||
|
"format": "{}",
|
||||||
|
"tooltip-format": "Click to switch between output devices",
|
||||||
|
"exec-on-event": true,
|
||||||
|
"exec": "$HOME/.config/waybar/scripts/output-devices.py --iconify",
|
||||||
|
"on-click": "$HOME/.config/waybar/scripts/output-devices.py --next --iconify",
|
||||||
|
"on-click-right": "$HOME/.config/waybar/scripts/output-devices.py --previous --iconify"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
138
hyprland/.config/waybar/scripts/output-devices.py
Executable file
138
hyprland/.config/waybar/scripts/output-devices.py
Executable file
@@ -0,0 +1,138 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import pprint
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def get_sinks():
|
||||||
|
"""
|
||||||
|
Consult pactl and get a sorted list of all sinks as dicts
|
||||||
|
"""
|
||||||
|
sinks = json.loads(
|
||||||
|
subprocess.run(
|
||||||
|
["pactl", "-f", "json", "list", "sinks"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
).stdout
|
||||||
|
)
|
||||||
|
return sorted(sinks, key=lambda s: s["index"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_status():
|
||||||
|
status = json.loads(
|
||||||
|
subprocess.run(
|
||||||
|
["pactl", "-f", "json", "info"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
).stdout
|
||||||
|
)
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
def change_sink(sink):
|
||||||
|
"""
|
||||||
|
Provided a sink object, use pactl to change over to it
|
||||||
|
"""
|
||||||
|
assert sink.get("name")
|
||||||
|
return subprocess.run(["pactl", "set-default-sink", sink.get("name")], check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_sink(sinks, status):
|
||||||
|
"""
|
||||||
|
Given a list of all sinks and the status of the system, return the dict
|
||||||
|
for the sink that's currently the default output device
|
||||||
|
"""
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
sink
|
||||||
|
for sink in sinks
|
||||||
|
if sink.get("name", "") == status.get("default_sink_name", "")
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_offset_sink(sinks, currentsink, offset=1):
|
||||||
|
def find_sink_by_index(sinks, index):
|
||||||
|
for sink in sinks:
|
||||||
|
if sink["index"] == index:
|
||||||
|
return sink
|
||||||
|
return None
|
||||||
|
|
||||||
|
"""
|
||||||
|
Given a list of sinks and the current default sink, get some offset in the
|
||||||
|
list of all sinks away from it. Useful for getting next/previous sinks
|
||||||
|
"""
|
||||||
|
indexes = [s["index"] for s in sinks]
|
||||||
|
i = indexes.index(currentsink.get("index", 0))
|
||||||
|
next_index = indexes[(i + offset) % len(indexes)]
|
||||||
|
return find_sink_by_index(sinks, next_index)
|
||||||
|
|
||||||
|
|
||||||
|
def get_sink_icon(sinks, currentsink):
|
||||||
|
"""
|
||||||
|
Given the list of all sinks and the current one, figure out how we should
|
||||||
|
display that as a small icon
|
||||||
|
"""
|
||||||
|
icon = {
|
||||||
|
"hdmi": "\uf26c", # fa-tv
|
||||||
|
"analog": "\uf028", # fa-volume-up
|
||||||
|
"headphones": "\uf025", # fa-headphones
|
||||||
|
"bluetooth": "\uf294", # fa-bluetooth-b
|
||||||
|
}
|
||||||
|
|
||||||
|
def classify(sink):
|
||||||
|
name = sink.get("name", "").lower()
|
||||||
|
port = sink.get("active_port", "")
|
||||||
|
desc = sink.get("description", "").lower()
|
||||||
|
if "hdmi" in name or "hdmi" in desc:
|
||||||
|
return "hdmi"
|
||||||
|
if "headphones" in port or "headset" in desc:
|
||||||
|
return "headphones"
|
||||||
|
if "analog" in name or "lineout" in port:
|
||||||
|
return "analog"
|
||||||
|
if "bluez" in name or "bluetooth" in desc:
|
||||||
|
return "bluetooth"
|
||||||
|
return "analog"
|
||||||
|
|
||||||
|
return icon[classify(currentsink)]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Display and cycle through PulseAudio and PipeWire sinks"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i",
|
||||||
|
"--iconify",
|
||||||
|
action="store_true",
|
||||||
|
help="Print an icon instead of the full name of the sink",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-n", "--next", action="store_true", help="Advance to the next sink"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-p", "--previous", action="store_true", help="Advance to the previous sink"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Get pactl's status
|
||||||
|
sinks = get_sinks()
|
||||||
|
status = get_status()
|
||||||
|
currentsink = get_current_sink(sinks, status)
|
||||||
|
# Figure out our offset into the list
|
||||||
|
offset = 1 if args.next else -1 if args.previous else 0
|
||||||
|
# Figure out where we wanna go
|
||||||
|
newsink = get_offset_sink(sinks, currentsink, offset)
|
||||||
|
change_sink(newsink)
|
||||||
|
if args.iconify:
|
||||||
|
print(get_sink_icon(sinks, newsink))
|
||||||
|
else:
|
||||||
|
print(newsink.get("description"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
@@ -222,3 +222,7 @@ window#waybar.fullscreen #window {
|
|||||||
color: rgba(235, 219, 178, 0.2);
|
color: rgba(235, 219, 178, 0.2);
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
}
|
}
|
||||||
|
#custom-output-device {
|
||||||
|
color: #ebdbb2;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user