Automatically turn off bluetooth headset after time out audio?

I have a Sony bluetooth headset connected to Ubuntu 20.04. They work very well, bluetooth can reconnect, I can even (mpris) control players from it.

The problem I have is that if I forget to turn the off while connected the headset will run out of battery overnight. I checked and there is nothing I can do from the headset side to automatically turn it off when there is no sound.

However the headset auto-turn off when there is no bluetooth connection. Therefore I think the only good option I have is if Ubuntu would disconnect the bluetooth after a certain period of silence.

Is there such option to auto disconnect a bluetooth audio device when there is no sound?

There is no much configuration I could find for the bluetooth device except this from the Settings window:

windowbt


After some searching around I came to the following solution based on this answer, which works when running PulseAudio:

Using the command pacmd list-sink-inputs one can see all the audio sinks. The output looks something like this: Example output of "pacmd list-sink-inputs"

Important for the solution here is state: RUNNING and sink: 31 <bluez_sink.[hardware address].a2dp_sink> because with this information one can check, if audio is currently streaming via Bluetooth or not. BlueZ is the technology running Linux's Bluetooth services. A sink that is connect but no music is played would have the following state state: CORKED

Based on this information one can now change the status of the Bluetooth adapter using the package bluez-tools. More specifically the command bt-adapter --set Powered false is used to turn of the the adapter when needed. Here is a link to all options and commans.

I wrote a bash script which sporadically checks if the PC is streaming audio via Bluetooth and if not turns off the Bluetooth adapter after a given timeout duration. Please forgive my scripting, its only the second script I've ever written in bash.

#!/bin/bash
deviceName="WH-1000XM3" # Name of the BT-Device
timeout=120              # Delay in seconds after which Device is disconnected
halfTimeout=$((timeout / 2))

# Checks if Sound is played via BT.
# Returns true if sound is play and false if not.
checkBTSound() {
  sinks=$(pacmd list-sink-inputs)  # Gets all sinks
  # Check if "bluez_sink" is in the list of sinks and if it also has
  # "state: RUNNING".
  [[ ("$sinks" == *"bluez_sink"*) &&\
   ("$sinks" == *"state: RUNNING"*) ]] && true || false
}

# Checks if specified BT-Device is connected
# Returns "true" if connected, "false" else.
deviceIsConnected() {
  response=$(bt-device -i $deviceName)
  [[ "$response" == *"Connected: 1"* ]] && true || false
}

# Endless Loop
while :
do
  counting=false
  inital=true
  notification=false
  start=1
  # Only take action, if device is connected and no sound is streaming.
  while
    (! checkBTSound && deviceIsConnected) && true || false 
  do
    # Sound has stopped playing. Start counting.
    counting=true

    # Only on first loop mark the time to calc the duration later.
    if $inital; then
      inital=false
      start=$SECONDS
    fi
 
    # Calculate time passed since first loop.
    timePassed=$(($SECONDS - $start))

    # Check if half of timeout time has passed and send notification if that is
    # the case and the notification has not already been sent.
    if [ $timePassed -ge $halfTimeout ] && ! $notification; then
      notify-send "Headphone Timeout"\
        "Headphone will be disconnected in $halfTimeout seconds."
      notification=true

    # Check if timout time has been surpassed and take corresponding actions if
    # that is the case.
    elif [ $timePassed -ge $timeout ]; then
      # Sending notification
      notify-send "Headphone Timeout"\
       "Disconnecting headphones \"$deviceName\"."
      # Disconnecting specified device
      bt-device -d $deviceName
      # Go back to outer loop waiting for device to connect again
      break
    fi
    sleep 1
  done
  sleep 5
done

Edit: You can also disconnect your device using

bt-device --disconnect=<Name of your Device>

That way you don't have to turn off the adapter. The name is the human readable Device-Name which you can also change to whatever you like in the Bluetooth settings.