Skip to content

Add Audio Output Switcher to Plugin Store - #1103

Open
zomars wants to merge 3 commits into
SteamDeckHomebrew:mainfrom
zomars:add-audio-output-switcher
Open

Add Audio Output Switcher to Plugin Store#1103
zomars wants to merge 3 commits into
SteamDeckHomebrew:mainfrom
zomars:add-audio-output-switcher

Conversation

@zomars

@zomars zomars commented Aug 9, 2026

Copy link
Copy Markdown

Add Audio Output Switcher to Plugin Store

Audio Output Switcher lets you change the default audio output device directly from the Quick Access Menu, without a trip to desktop mode. This is especially useful on devices where gamemode has no output picker at all — e.g. on a Steam Machine driving a TV, the QAM audio section is only a CEC volume slider.

What it does:

  • Lists every available audio output, with the active device marked and pinned to the top.
  • Switches the output with one tap, and already-playing streams follow the switch immediately rather than only new ones.
  • Adds a "Follow system default" row while an override is set, so there is a way back from a pinned device.
  • Optional controller shortcut: a button combo you record yourself cycles the output without opening the panel. Nothing is bound by default.
  • Device arrival and removal are events, so a wireless headset connecting or powering off shows up right away without reloading the panel.

The backend is the standard Python backend and declares no flags, so it runs as the ordinary user rather than root — no bundled binaries and no subprocesses. Switching goes through SteamClient.System.Audio, the same API Steam's own audio selector uses; the Python side exists only to persist the shortcut binding across restarts.

Plugin panel

Submodule is pinned at ff2cc4b (v1.2.0).

Task Checklist

Developer

  • I am the original author or an authorized maintainer of this plugin.
  • I have abided by the licenses of the libraries I am utilizing, including attaching license notices where appropriate.
  • Generative AI was NOT used to write a majority of the code I am submitting.

Plugin

  • I have verified that my plugin works properly on the Stable and Beta update channels of SteamOS.
  • I have verified my plugin is unique or provides more/alternative functionality to a plugin already on the store.

Backend

  • No: I am using a custom backend other than Python.
  • No: I am using a tool or software from a 3rd party FOSS project that does not have it's dependencies statically linked.
  • No: I am using a custom binary that has all of it's dependencies statically linked.

Community

  • I have tested and left feedback on two other pull requests for new or updating plugins.
  • I have commented links to my testing report in this PR.

Testing

  • Tested by a third party on SteamOS Stable or Beta update channel.

@github-actions github-actions Bot added the not-plugin Not related to plugins themselves label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Issues Found

  • Neither testing box is present in your description. Please include one of them per the Markdown comment above the testing section.

Next Steps

  1. If we found any issues above, please edit your pull request description to resolve them and leave a comment saying you've done so.
  2. For the quickest review, please see the Community section of the pull request template for how you can help other developers.
  3. Once your description is correct, a maintainer will review your pull request as soon as possible.

Thank you for your contribution! If you need any help, please reach out on our Discord server. ❤️

@github-actions

Copy link
Copy Markdown

This pull request is stale as no updates or testing reports have been posted within the last 7 days. Please ensure you are actively recruiting testers or resolving the issues discussed. If you do not remove the stale label or comment, this will be closed in 7 days. Please close this pull request if you need more time to resolve issues so we can keep our repo healthy.

@github-actions github-actions Bot added stale plugin-addition Adding a plugin to the Plugin Store labels Aug 17, 2026
@zomars
zomars marked this pull request as ready for review August 17, 2026 23:28
@zomars
zomars requested a review from a team as a code owner August 17, 2026 23:28
@github-actions github-actions Bot removed the stale label Aug 18, 2026
@zomars

zomars commented Aug 18, 2026

Copy link
Copy Markdown
Author

Testing reports I left on other plugin pull requests, per the Community checklist:

Both were tested on SteamOS Stable with Decky Loader 3.2.6.

Picks up the async pactl rewrite, the derived PipeWire runtime dir, and the
titleView fix.
@github-actions

Copy link
Copy Markdown

This pull request is stale as no updates or testing reports have been posted within the last 7 days. Please ensure you are actively recruiting testers or resolving the issues discussed. If you do not remove the stale label or comment, this will be closed in 7 days. Please close this pull request if you need more time to resolve issues so we can keep our repo healthy.

@github-actions github-actions Bot added the stale label Aug 26, 2026
@zomars

zomars commented Aug 26, 2026

Copy link
Copy Markdown
Author

Pin updated to v1.2.0 (ff2cc4b); description updated to match.

Switching now goes through SteamClient.System.Audio instead of pactl:

  • Playing streams follow the switch, handled by Steam.
  • Virtual sinks such as steam-streaming-playback are no longer listed.
  • Device add/remove are events, not a four-second poll.
  • New "Follow system default" row clears an override.
  • The Python backend only persists the shortcut binding; no subprocesses.

Still needs a third-party tester. Build plugin (AudioOutputSwitcher, true) has been waiting for maintainer approval since 18 Aug, so there is no CI build to install.

@AAGaming00 AAGaming00 removed the not-plugin Not related to plugins themselves label Aug 26, 2026
@Outpox

Outpox commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Testing report

Tested on a Steam Deck LCD, SteamOS Stable, Decky Loader v3.2.6, at the pinned commit ff2cc4b.

Built it from source: pnpm build and tsc --noEmit are clean, and the rebuilt dist/index.js is
byte-identical to the committed one. Installed it and drove the panel on hardware.

What worked

  • Switching outputs from the panel. pactl get-default-sink followed every tap, and a live audio
    stream moved across with it, no manual move-sink-input needed. That part is genuinely nice.
  • "Follow system default" appears only when an override is set, clears it correctly, and disappears again.
  • Device add and remove are live. I removed a sink with the panel open and it vanished without a reload.
  • Backend loads and unloads cleanly, nothing in the logs, no root needed.

Three things I would fix

  1. Closing the panel mid-capture leaves the capture armed. Nothing clears the capture global
    on unmount. Start recording a combo, close the QAM with the button (which by your own README
    never reaches the input stream), and the panel looks idle on reopen but is not: the shortcut
    stops firing, and the next two buttons held together in a game get saved as the new binding.
    I reproduced it with your own onButton / startCapture code: a saved L4+R4 binding silently
    became A+B. Clearing capture in the effect cleanup, plus a timeout, should cover it.

  2. On a plain Deck with nothing plugged in, the panel shows one disabled row and no explanation.
    PipeWire exposes Speaker and Headphones as separate sinks, but Steam collapses them into a single
    device, so SetDefaultDeviceOverride cannot reach that distinction. Your Steam Machine case is
    real and the README explains it well, but publish.description promises "every available audio
    output", which reads wrong on the device most store users own. Worth naming the audience in the
    description, and showing a line like "Only one output available, connect a headset, dock or TV"
    instead of a bare disabled row.

  3. The active device is not pinned to the top, contrary to the PR body and README. It is marked
    with , but the list is sorted alphabetically only. With a second device active it sat third.
    Either implement it or drop the claim. Note cycleOutput walks the same array, so pinning would
    change the shortcut's cycle order too.

Smaller stuff: the README still says "repolls" in the features list where the code uses events, and
plain virtual sinks do show up in GetDevices() (two module-null-sink sinks appeared with
bHasOutput: true), so the "virtual sinks never appear" note is stated more strongly than it holds.

@github-actions github-actions Bot removed the stale label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin-addition Adding a plugin to the Plugin Store

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants