No description
  • JavaScript 80.2%
  • Nix 19.8%
Find a file
Andreas Rammhold 01e552a656 Add AVR power-off action and robust auto-off
- Menu action to power the AVR off when it is on but not the
  selected output (e.g. left on from an earlier session).
- Auto-off on explicit move-away now also fires when we last
  selected the AVR, not only when its sink is still the default.
- CLI avr-off command; VM mock tracks on/off state.
2026-07-10 12:37:12 +02:00
nix Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00
scripts Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00
src Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00
tests Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00
.envrc Initial GNOME audio switcher extension 2026-07-06 14:32:37 +02:00
.gitignore Initial GNOME audio switcher extension 2026-07-06 14:32:37 +02:00
CLAUDE.md Initial GNOME audio switcher extension 2026-07-06 14:32:37 +02:00
eslint.config.js Add headless CLI harness; drop unusable nested script 2026-07-06 15:24:32 +02:00
flake.lock Initial GNOME audio switcher extension 2026-07-06 14:32:37 +02:00
flake.nix Add headless CLI harness; drop unusable nested script 2026-07-06 15:24:32 +02:00
README.md Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00
todo.md Add AVR power-off action and robust auto-off 2026-07-10 12:37:12 +02:00

Audio Switcher

A GNOME Shell extension that switches the default audio output between three targets from a panel menu:

  • Scarlett — the USB audio interface.
  • Bluetooth (Sony) — powers on Bluetooth, connects the configured headphones, then routes audio to them.
  • AV Receiver — powers the amplifier on via Home Assistant, switches it to the configured input source, and shows its volume slider directly in the menu.

When the AV receiver is powered on but is not the selected output (e.g. left on from an earlier session), the menu shows a Power off AV receiver action so you can shut it down to save energy without switching to it first.

Audio is switched with WirePlumber (wpctl set-default), so it targets a PipeWire session. GNOME Shell 45+ (tested on 50).

Configuration

Open the extension preferences (or gnome-extensions prefs audio-switcher@rammhold.de) and set:

Setting Example
Scarlett sink match Scarlett
AVR sink match S/PDIF
Headphones MAC AA:BB:CC:DD:EE:FF
Home Assistant base URL http://homeassistant.local:8123
Home Assistant token long-lived access token
AVR media_player entity media_player.avr
AVR input source DVR

The Scarlett and AVR sink matches are case-insensitive substrings compared against the names shown by wpctl status. The Bluetooth output is located by the headphones' MAC (via the bluez pw-dump node name), so it needs no sink match — only the correct MAC.

Development

The dev environment is provided by the flake; direnv allow (or nix develop) drops you into a shell with gjs, glib, eslint and nixpkgs-fmt.

gjs -m tests/parse.test.js          # unit tests
eslint src tests                    # lint
nixpkgs-fmt flake.nix nix/*.nix     # format nix
nix flake check                     # unit + VM test + eval

Testing

The logic, right now (no shell, no restart)

The src/lib modules only import gi, so they run under bare gjs and drive the real hardware. scripts/cli.js exercises them:

nix develop --command gjs -m scripts/cli.js status      # list sinks + current default
nix develop --command gjs -m scripts/cli.js scarlett    # switch default to the Scarlett
nix develop --command gjs -m scripts/cli.js bluetooth   # connect headphones + route
HASS_URL=http://ha:8123 HASS_TOKEN=... HASS_ENTITY=media_player.avr \
  nix develop --command gjs -m scripts/cli.js avr       # power-on + select source + route + read volume
HASS_URL=http://ha:8123 HASS_TOKEN=... HASS_ENTITY=media_player.avr \
  nix develop --command gjs -m scripts/cli.js avr-off   # power the AVR off

Sink matches default to the gschema defaults and can be overridden with AUDIO_SCARLETT_MATCH / AUDIO_AVR_MATCH; the Bluetooth device is selected by BT_MAC. The AVR input source defaults to DVR and can be overridden with HASS_SOURCE.

The panel widget

GNOME Shell only scans the extensions directory at session start and Wayland has no live-reload, so the widget appears after the shell re-inits. Install it (nix profile install . or the symlink below) then log out and back in and:

gnome-extensions enable audio-switcher@rammhold.de
gnome-extensions prefs audio-switcher@rammhold.de     # settings (runs in its own process)
journalctl --user -f -o cat /usr/bin/gnome-shell      # watch logError() output

For a symlinked dev install that tracks the working tree:

ln -sfn "$PWD/src" ~/.local/share/gnome-shell/extensions/audio-switcher@rammhold.de
nix develop --command glib-compile-schemas src/schemas

A nested shell (gnome-shell --wayland) is not usable here: this mutter build runs it as a full display server and fails to take the seat. The NixOS VM test (nix build .#checks.x86_64-linux.vm) is the way to exercise the full widget headlessly.

Nix

nix build                           # build the extension package
nix build .#checks.x86_64-linux.vm  # run the NixOS VM test (heavy: builds GNOME)

The package installs into share/gnome-shell/extensions/audio-switcher@rammhold.de/; add it to environment.systemPackages on NixOS and enable it with gnome-extensions enable audio-switcher@rammhold.de.