'Commands to control the volume of a connected bluetooth device in Linux? [closed]

Let's say I have paired and connected a Bluetooth headset successfully to my Linux system.

I know I can check my Bluetooth device MAC id from the bluetoothctl command.

Is there a way to control the Bluetooth device volume using some commands in Linux?



Solution 1:[1]

With this method, you need the appropriate D-Bus object path for the device to be controlled. There are a few ways of finding that (like using D-Feet), but once you have it, the following command will adjust the volume:

dbus-send --print-reply --system --dest=org.bluez /org/bluez/xxxx/yyyy/dev_zz_zz_zz_zz_zz_zz org.bluez.Control.VolumeUp

where "xxxx" seems to be the PID for bluetoothd, "yyyy" is the adapter (like "hci0"), "zz_zz_zz..." represents the MAC address of the controlled device (headset, speakers, etc.) separated by underscores, and 'VolumeUp' is replaced with 'VolumeDown' to decrease volume.

See the D-Bus documentation for more helpful on properly finding the object path. To do more than just adjust the volume, see the Bluez API documentation.

Solution 2:[2]

Not a proper answer, but an explanation: there are 2 different volumes for most bluetooth headsets: the software volume and the hardware volume. Traditionally on Linux you only control the software, however headsets can have an additional internal hardware volume, which is not possible to control currently. There are bug reports in place:

If somebody knows how to control the hardware internal volume please reply below.

Solution 3:[3]

You can set the device's internal volume to a specific value instead of incrementing up or down using the shell:

  1. Find the MAC address of your BT headset (we will replace colon with underscore for DBus-compatible MAC values):
$ bluetoothctl devices | sed "s/:/_/g"
Device A1_B2_C3_D4_E5_F6 Some Headset
  1. Find the DBus object that allows control of the headset's internal volume:
$ dbus-send --system --print-reply --dest=org.bluez / org.freedesktop.DBus.ObjectManager.GetManagedObjects | less
  1. While it is piped to less, search for the interface org.bluez.MediaTransport1 (press /, type MediaControl1, press Enter) and record the value of object path:
object path "/org/bluez/hci0/dev_A1_B2_C3_D4_E5_F6/sep1/fd0"
  array [
    dict entry(
      string "org.bluez.MediaTransport1"

You should see a property named Volume in that block a few lines down (if it is not present, the headset object does not support setting the volume directly):

dict entry(
  string     "Volume"
  variant    uint16 90
  1. First get the current volume (here reported as 60):
$ dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_A1_B2_C3_D4_E5_F6/sep1/fd0 org.freedesktop.DBus.Properties.Get string:"org.bluez.MediaTransport1" string:"Volume" | grep uint16 | awk '{print $3}'
60
  1. Let's increment the volume by ten - DO NOT GO STRAIGHT TO MAXIMUM OR LARGE VALUES as this may damage your equipment and/or your hearing.
$ dbus-send --system --dest=org.bluez /org/bluez/hci0/dev_A1_B2_C3_D4_E5_F6/sep1/fd0 org.freedesktop.DBus.Properties.Set string:"org.bluez.MediaTransport1" string:"Volume" variant:uint16:70

$ dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0/dev_A1_B2_C3_D4_E5_F6/sep1/fd0 org.freedesktop.DBus.Properties.Get string:"org.bluez.MediaTransport1" string:"Volume" | grep uint16 | awk '{print $3}'
70

On my equipment the range appears to be 0-127, with some values over 110 producing distortion. Take care with the values you supply to avoid damage.

Solution 4:[4]

I wanted to do device enumeration via PulseAudio as I have more than just Bluetooth devices I want to control. This relies on Sean's suggestion to use DBus to actually push the commands out to the Bluetooth devices. This script works in case you have multiple Bluetooth audio devices.

The Python library pulsectl makes finding these device identifiers easy. Sean's suggestion to use D-Feet was great to figure out the updated method name.

A mostly complete example is:

from pulsectl import Pulse
import subprocess

# diff applies to non-BT Pulse devices
diff = 0.1
method = 'org.bluez.MediaControl1.Volume{}'.format(
  'Down' if diff < 0 else 'Up')

with Pulse() as pulse:
  for sink in pulse.sink_list():
    bluez_path = sink.proplist.get('bluez.path')
    if bluez_path:
        args = [
            'dbus-send', '--system', '--print-reply',
            '--dest=org.bluez', bluez_path, method,
        ]
        subprocess.run(args, check=True)
    else:
        pulse.volume_change_all_chans(sink, diff)

I have something similar to this with diff coming from the command line, allowing relatively sane control over volume levels.

Solution 5:[5]

I used my i-phone to set the hardware volume in my Sony Headphones to MAX. Afterwords control for volume in Ubunto 20.04 LTS works fine.

Steps

  1. Disconnect Blue-Tooth headphones from Ubuntu
  2. Connect headphones to i-phone via Blue-Tooth
  3. Open a YouTube video and adjust volume all the way up.
  4. Disconnect headphones from i-phone
  5. Connect Blue-Tooth headphones to your Ubuntu machine

The sound should now be fully adjustable.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sean Normandy
Solution 2 Smeterlink
Solution 3 wovano
Solution 4 Carolus
Solution 5 James Stallings