From c64766acea7bbebe8b9bdb59abe45600f7c8d00a Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 29 Dec 2025 16:14:02 +0100 Subject: [PATCH 1/2] nixos/alsa: add bluetooth support --- nixos/modules/services/audio/alsa.nix | 50 ++++++++++++++++++++++++-- pkgs/by-name/bl/bluez-alsa/package.nix | 13 ++++--- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/nixos/modules/services/audio/alsa.nix b/nixos/modules/services/audio/alsa.nix index c1ee7023aa1f..7bd0200e3c02 100644 --- a/nixos/modules/services/audio/alsa.nix +++ b/nixos/modules/services/audio/alsa.nix @@ -44,9 +44,15 @@ let in lib.forEach options (i: "options ${i.driver} index=${toList i.ids} id=${toList i.names}"); - defaultDeviceVars = { + pluginsPath = pkgs.symlinkJoin { + name = "alsa-with-plugins"; + paths = cfg.plugins; + }; + + alsaVariables = { "ALSA_AUDIO_OUT" = cfg.defaultDevice.playback; "ALSA_AUDIO_IN" = cfg.defaultDevice.capture; + "ALSA_PLUGIN_DIR" = lib.mkIf (cfg.plugins != [ ]) "${pluginsPath}/lib/alsa-lib"; }; in @@ -106,6 +112,8 @@ in enableOSSEmulation = lib.mkEnableOption "the OSS emulation"; + enableBluetooth = lib.mkEnableOption "Bluetooth audio support via BlueALSA"; + enableRecorder = lib.mkOption { type = lib.types.bool; default = false; @@ -125,6 +133,15 @@ in ''; }; + plugins = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + example = lib.literalExpression "[ pkgs.bluez-alsa ]"; + description = '' + List of ALSA plugins to be added to the search path. + ''; + }; + defaultDevice.playback = lib.mkOption { type = lib.types.str; default = ""; @@ -392,8 +409,8 @@ in }; # Set default PCM devices - environment.sessionVariables = defaultDeviceVars; - systemd.globalEnvironment = defaultDeviceVars; + environment.sessionVariables = alsaVariables; + systemd.globalEnvironment = alsaVariables; environment.etc."asound.conf".text = cfg.config; @@ -412,6 +429,33 @@ in environment.systemPackages = [ pkgs.alsa-utils ]; }) + (lib.mkIf (cfg.enable && cfg.enableBluetooth) { + + users.users.bluealsa = { + description = "BlueALSA daemons user"; + isSystemUser = true; + group = "audio"; + }; + + # Link ALSA configuration + environment.etc."alsa/conf.d/20-bluealsa.conf".source = + "${pkgs.bluez-alsa}/etc/alsa/conf.d/20-bluealsa.conf"; + + # Install plugin + hardware.alsa.plugins = [ pkgs.bluez-alsa ]; + + # Install CLI tools and systemd units + environment.systemPackages = [ pkgs.bluez-alsa ]; + systemd.packages = [ pkgs.bluez-alsa ]; + + # See Nixpkgs issue #81138 + systemd.services."bluealsa".wantedBy = [ "bluetooth.target" ]; + + # Note: bluealsa-aplay is available but we don't start it + # by default, it's only needed to make the machine act as + # bluetooth speaker + }) + (lib.mkIf config.hardware.alsa.enablePersistence { # Install udev rules for restoring card settings on boot diff --git a/pkgs/by-name/bl/bluez-alsa/package.nix b/pkgs/by-name/bl/bluez-alsa/package.nix index bc7500df3b5b..3bdce7e545e1 100644 --- a/pkgs/by-name/bl/bluez-alsa/package.nix +++ b/pkgs/by-name/bl/bluez-alsa/package.nix @@ -16,6 +16,8 @@ readline, sbc, python3, + systemdSupport ? true, + systemdLibs, }: stdenv.mkDerivation (finalAttrs: { @@ -37,7 +39,8 @@ stdenv.mkDerivation (finalAttrs: { autoreconfHook pkg-config python3 - ]; + ] + ++ lib.optional systemdSupport systemdLibs; buildInputs = [ alsa-lib @@ -49,9 +52,7 @@ stdenv.mkDerivation (finalAttrs: { libbsd ncurses ] - ++ lib.optionals aacSupport [ - fdk_aac - ]; + ++ lib.optional aacSupport fdk_aac; configureFlags = [ (lib.enableFeature aacSupport "aac") @@ -59,6 +60,10 @@ stdenv.mkDerivation (finalAttrs: { (lib.enableFeature true "rfcomm") (lib.withFeatureAs true "alsaplugindir" "${placeholder "out"}/lib/alsa-lib") (lib.withFeatureAs true "dbusconfdir" "${placeholder "out"}/share/dbus-1/system.d") + (lib.enableFeature systemdSupport "systemd") + (lib.withFeatureAs systemdSupport "systemdsystemunitdir" "${placeholder "out"}/lib/systemd/system") + (lib.withFeatureAs systemdSupport "bluealsauser" "bluealsa") + (lib.withFeatureAs systemdSupport "bluealsaaplayuser" "bluealsa") ]; passthru.updateScript = gitUpdater { }; From 5618bfce361200c93db38a0c6ae2bd748b791366 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Sat, 17 Jan 2026 00:25:58 +0100 Subject: [PATCH 2/2] nixos/release-notes: mention hardware.alsa.enableBluetooth --- nixos/doc/manual/release-notes/rl-2605.section.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index aa50affb51a2..4dd7e05f972d 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -128,6 +128,8 @@ See . - [services.resolved](#opt-services.resolved.enable) module was converted to RFC42-style settings. The moved options have also been renamed to match the upstream names. Aliases mean current configs will continue to function, but users should move to the new options as convenient. +- Support for Bluetooth audio based on `bluez-alsa` has been added to the `hardware.alsa` module. It can be enabled with the new [enableBluetooth](#opt-hardware.alsa.enableBluetooth) option. + - `services.openssh` now supports generating host SSH keys by setting `services.openssh.generateHostKeys = true` while leaving `services.openssh.enable` disabled. This is particularly useful for systems that have no need of an SSH daemon but want SSH host keys for other purposes such as using agenix or sops-nix. - `services.slurm` now supports slurmrestd usage through the `services.slurm.rest` NixOS options.