feat(audio): prefer dock/HDMI/USB sinks in WirePlumber (#87)
Some checks failed
Check / eval (push) Has been cancelled

Raise priority.session for HDMI/DisplayPort (1100) and USB (1050) ALSA
outputs so the default sink follows dock plug/unplug; built-in analog
wins again when externals disappear. Rules live in
modules/nixos/dock-audio-rules.nix; checks.dock-audio covers the contract.

Verified: V2 — flake check --no-build; checks.dock-audio green.
V3 pending: HARDWARE-QUEUE #87 real HDMI/USB-dock hotplug.
This commit is contained in:
Bernardo Magri
2026-07-11 10:36:07 +01:00
parent 27f18e4ccc
commit f47903a6a6
9 changed files with 125 additions and 8 deletions

View File

@@ -510,6 +510,61 @@
'';
};
# Dock audio sink priority (#87): WirePlumber rules prefer
# HDMI/DP/USB sinks over built-in analog. Pure unit test of the
# rule set + a minimal VM that the conf file is installed where
# WirePlumber reads it (XDG_DATA_DIRS / share/wireplumber).
dock-audio =
let
inherit (nixpkgs) lib;
rules = import ./modules/nixos/dock-audio-rules.nix;
alsa = rules."monitor.alsa.rules" or [ ];
expect = cond: msg: lib.assertMsg cond "dock-audio: ${msg}";
json = builtins.toJSON alsa;
pureOk =
expect (builtins.isList alsa && builtins.length alsa >= 2)
"expected 2 alsa rule groups"
&& expect (lib.hasInfix "hdmi" json)
"HDMI match missing from rules JSON"
&& expect (lib.hasInfix "usb-" json || lib.hasInfix "usb" json)
"USB match missing from rules JSON"
&& expect (lib.hasInfix "priority.session" json)
"priority.session missing"
&& expect (lib.hasInfix "1100" json)
"HDMI session priority 1100 missing";
in
assert pureOk;
pkgs.testers.runNixOSTest {
name = "nomarchy-dock-audio";
nodes.machine = { ... }: {
services.pipewire = {
enable = true;
alsa.enable = true;
pulse.enable = true;
wireplumber.enable = true;
wireplumber.extraConfig."90-nomarchy-dock-audio" =
import ./modules/nixos/dock-audio-rules.nix;
};
# WirePlumber is a user service; enable linger-less check via
# the system data dir the package puts conf into.
environment.systemPackages = [ pkgs.wireplumber ];
};
testScript = ''
machine.wait_for_unit("multi-user.target")
conf = machine.succeed(
"find /nix/store -path "
"'*wireplumber.conf.d/90-nomarchy-dock-audio.conf' "
"2>/dev/null | head -1"
).strip()
assert conf, "90-nomarchy-dock-audio.conf not in the store"
body = machine.succeed(f"cat '{conf}'")
assert "monitor.alsa.rules" in body, body
assert "priority.session" in body, body
assert "hdmi" in body.lower() or "HDMI" in body, body
assert "usb" in body.lower(), body
'';
};
# Clamshell / dock lid policy (#86): logind must ignore lid close
# when docked (external display present). Real multi-monitor lid
# events stay V3; this asserts the baked logind.conf + that