Multipoint Bluetooth Headphones stuck to device with NixOS

I have bluetooth headphones that support multipoint (multiple devices connected at once), and with other devices it works perfectly as advertised. For example, when I pause audio on my PC, I can play audio on my phone and the headphones switch audio source. However, on my NixOS setup (with KDE Plasma 6), pausing audio does not allow other devices to “take over.”

I can confirm that multipoint works properly, on these headphones, between:

  • Same hardware running Ubuntu + Phone
  • Same hardware running Windows + Phone
  • Other device + Phone

My only guess is that either KDE or NixOS is keeping the audio channel “active,” preventing device switching (the headphones won’t switch devices if one device keeps playing audio). Below is some of my configuration. Any help would be greatly appreciated, thanks!

  hardware.pulseaudio.enable = false;
  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
  };

  # Enable bluetooth
  hardware.bluetooth.enable = true; # enables support for Bluetooth
  hardware.bluetooth.powerOnBoot = true; # powers up the default Bluetooth controller on boot
  # A2DP
  hardware.bluetooth.settings = {
    General = {
      Enable = "Source,Sink,Media,Socket";
      FastConnectable = "true";
      MultiProfile = "multiple";
      Experimental = true;
    };
  };
1 Like

I am experiencing the same issue with my WH-1000XM4.

I also noticed that the “take headphones off to pause” is not working quite right. If I start playing music by pressing “play” on the headphones, it works, but if I start it from the computer side it stops working. It seems that the headphones still think that the music is in a paused state and don’t send the necessary pause command, and I’ve confirmed that by looking at the HCI logs. It works with my Android phone, so I’m currently trying to capture the HCI logs there and see what the difference is - I suspect the phone is sending some kind of information about the current status of media playback (whether it is playing or paused), and this fixes both the multipoint issue and the take-off-to-pause issue.

Actually, multipoint switching just magically started to work, even without a reboot. I don’t know the reason for it. Take-off-to-pause is still broken, though.

I figured out the take-off-to-pause thing as well; I just needed to run mpris-proxy (services.mpris-proxy.enable = true with home-manager) and it magically fixed it (I’m assuming it is sending player status information to the headphones via MPRIS). It may also help with multipoint, but I’m not certain.

Yeah it got better for me too lately, must have been an update.

But, I always get this: Failed to connect: org.bluez.Error.Failed br-connection-create-socket

If I power cycle bluetoothctl it connects automatically.

Figured USB Bluetooth 5.3 wasn’t supported.

I just created a script to power cycle, but would be nice if trusting would allow auto-connect.

  # Bluetooth support
  hardware = {
    bluetooth = {
      enable = true;
      package = pkgs-unstable.bluez5-experimental;
      powerOnBoot = true;
      settings = {
        General = {
          Enable = "Source,Sink,Media,Socket";
          Experimental = true; # Enable experimental features
          FastConnectable = true; # Improve connection speed
          JustWorksRepairing = "always";
          controllerMode = "bredr"; # Allow low energy mode?
          MultiProfile = "multiple"; # Allow multiple profiles
          AutoEnable = true;
        };
      };
    };
  };

Script

{ pkgs }:

pkgs.writeShellScriptBin "bluetoothSwitch" ''
  device="74:74:46:1C:20:61"
  max_attempts=3
  
  check_controller() {
    if ! bluetoothctl show | grep 'Powered: yes' -q; then
      echo "Controller is powered off. Powering on..."
      bluetoothctl power on
      sleep 2
    fi
  }
  
  connect_device() {
    bluetoothctl connect "$device"
    sleep 2
    bluetoothctl info "$device" | grep 'Connected: yes' -q
  }
  
  cycle_bluetooth() {
    bluetoothctl power off
    sleep 2
    bluetoothctl power on
    sleep 2
  }
  
  # Ensure controller is powered on first
  check_controller
  
  # Check current connection state
  if bluetoothctl info "$device" | grep 'Connected: yes' -q; then
    echo "Device is connected. Disconnecting..."
    bluetoothctl disconnect "$device"
  else
    echo "Device not connected. Attempting to connect..."
    
    attempt=1
    while [ $attempt -le $max_attempts ]; do
      if connect_device; then
        echo "Successfully connected on attempt $attempt"
        exit 0
      fi
      
      echo "Connection attempt $attempt failed"
      cycle_bluetooth
      attempt=$((attempt + 1))
    done
    
    echo "Failed to connect after $max_attempts attempts"
    exit 1
  fi
''