35

Is there a way, through powershell, or any other tools to connect and disconnect a paired Bluetooth device? Basically press the Connect/Disconnect button in Bluetooth & other devices, except from command line of whatever sort (Powershell, bat using some command line tools, code in C#, C++, etc.):

Bluetooth & other devices

I found this answer but it involves unpairing and pairing again, which won't work because my headphones need to be in pairing mode to accept new pairing. I'd rather not simulate keypresses and mouse clicks via AutoIt or some other software like that.

MarcinJ
  • 861

10 Answers10

31

I wrote C++ code to do it using Win32 Bluetooth API's BluetoothSetServiceState, but it's actually enough to use Bluetooth Command Line Tools.

As it turns out, once all services in use by a device get disabled, device gets released and disconnected by Windows automatically. In my case these are voice and music, as per the screenshot, and most headphones will work the same way.
Voice is actually the hands free service (HFP) and music is just an audio sink (A2DP). Service identifiers will be necessary and they can be discovered through the usage of btdiscovery command from the package above, or via the list of Bluetooth services. HFP voice is 111e, A2DP music is 110b.

Per btcom command line help:

Usage:

btcom {-c|-r} {-bBluetoothAddress | -nFriendlyName} [-s{sp|dun|GUID|UUID}]

 -c  Create association between COM port and a remote service (Enable non-COM service).
 -r  Remove association between COM port and a remote service (Disable non-COM service).
 -s  Remote service to use (Default is Serial Port Service)
 -b  Bluetooth address of remote device in (XX:XX:XX:XX:XX:XX) format. 
 -n  Friendly name of remote device.

To disconnect the device, issue the following (only works when run as administrator in my case, using Windows 10 1809 (17763.437)):

"C:\Program Files (x86)\Bluetooth Command Line Tools\bin\btcom" -n "WH-1000XM3" -r -s111e
"C:\Program Files (x86)\Bluetooth Command Line Tools\bin\btcom" -n "WH-1000XM3" -r -s110b

To connect again, issue the same with -c instead of -r. This works for other devices, not just headphones, as long as all services/profiles connected to by Windows get disabled/enabled.

Note: using -n <friendly name> is much slower than using -b <address> due to performing Bluetooth discovery.

MarcinJ
  • 861
10

win 10 shortcuts https://www.windowscentral.com/best-windows-10-keyboard-shortcuts

win-key + K opens the sidebar menu and instantly searches for bluetooth devices. then click on your already paired device and connect.

7

In case the solution from @MarcinJ with Bluetooth Command Line Tools is too slow for you, especially in case you want to pair the device meanwhile an incoming call try creating a windows shortcut:
(Right click > New > Shortcut)

%windir%\explorer.exe ms-settings-connectabledevices:devicediscovery

It will pop up a display and audio panel right on the screen, which is one click away from connect.
And nice BT icon for the shortcut can be found for example on C:\Windows\System32\fsquirt.exe.

5

The AutoHotkey script brought up by Pavel is most inspiring, yet compilation requires AutoHotkey. I ported the code to powershell, lest the code requires AutoHotkey:

Param(
                [Parameter(Mandatory=$False,Position=1)]
                [string]$dnp
)

if($dnp -eq "/?") { Write-Host "usage: $($MyInvocation.InvocationName) [device name]" exit }

if(!$dnp) { $dnp="WH-1000XM4" }

$devices = Get-ChildItem -Path HKLM:\SYSTEM\ControlSet001\Services\BTHPORT\Parameters\Devices foreach($device in $devices) { $address=$device.pschildname.ToUpper() $name=$device.GetValue("Name") if($name -ne $null) { $printableName = ($name -notmatch 0 | ForEach{[char]$_}) -join "" if($printableName -eq $dnp) { $ma=$address } } }

$gui="1" if(!$ma) { Write-Host "Device $dnp not found.rnStop." if($gui -eq "1") {[System.Windows.Forms.MessageBox]::Show("Device $dnp not found.","Error")} exit }

Write-Verbose "Working with the device $dnp and with the address $ma."

$id=get-random Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)] public struct BLUETOOTH_DEVICE_INFO$id { public UInt32 dwSize; public UInt64 Address; public UInt64 ulClassofDevice; public bool fConnected; public bool fRemembered; public bool fAuthenticated; public decimal stLastSeen; //decimal is just as long as SystemTime public decimal stLastUsed; //decimal is just as long as SystemTime public string szName; }

namespace bluco { public class Program$id { [DllImport("bthprops.cpl", CharSet = CharSet.Auto, SetLastError = true)] static extern uint BluetoothSetServiceState(IntPtr hRadio, ref BLUETOOTH_DEVICE_INFO$id DeviceInfo, ref Guid guid, int ServiceFlags);

    public static void Main() {
        BLUETOOTH_DEVICE_INFO$id mdi = new BLUETOOTH_DEVICE_INFO$id {
            // a 0x424, 1060 error is produced if the below address is wrong, that number seems to indicate ERROR_SERVICE_DOES_NOT_EXIST
            Address = Convert.ToUInt64(&quot;$ma&quot;, 16),
            szName = &quot;WH-1000XM5&quot;,
            // 72 is the size ofBLUETOOTH_DEVICE_INFO$id without the string szName
            // according to the specification, the string szName has at most 248 wchars
            dwSize = (uint) (72 + 4*248)
        };

        Guid mUUID = new Guid(&quot;0000111e-0000-1000-8000-00805f9b34fb&quot;); // https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/
        uint hr0 = BluetoothSetServiceState(IntPtr.Zero, ref mdi, ref mUUID, 1);
        mUUID = new Guid(&quot;0000110b-0000-1000-8000-00805f9b34fb&quot;); // https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/
        uint hr1 = BluetoothSetServiceState(IntPtr.Zero, ref mdi, ref mUUID, 1);
        if(hr0 == 0 &amp;&amp; hr1 == 0) {
            Console.WriteLine(&quot;Bluetooth connect.&quot;);
            #pragma warning disable 162
            if(&quot;$gui&quot;==&quot;1&quot;) System.Windows.Forms.MessageBox.Show(&quot;Bluetooth connect.&quot;,&quot;Connect&quot;);
            #pragma warning restore 162
        }
        if(hr0 != 0 || hr1 != 0) {
            mUUID = new Guid(&quot;0000111e-0000-1000-8000-00805f9b34fb&quot;);
            hr0 = BluetoothSetServiceState(IntPtr.Zero, ref mdi, ref mUUID, 0);
            // a 0x424, 1060 error is produced if the below address is wrong, that number seems to indicate ERROR_SERVICE_DOES_NOT_EXIST
            mUUID = new Guid(&quot;0000110b-0000-1000-8000-00805f9b34fb&quot;);
            hr1 = BluetoothSetServiceState(IntPtr.Zero, ref mdi, ref mUUID, 0);
            if(hr0 == 0 &amp;&amp; hr1 == 0) {
                Console.WriteLine(&quot;Bluetooth disconnect.&quot;);
                #pragma warning disable 162
                if(&quot;$gui&quot;==&quot;1&quot;) System.Windows.Forms.MessageBox.Show(&quot;Bluetooth disconnect.&quot;,&quot;Disconnect&quot;);
                #pragma warning restore 162
            }
        }
    }
}

} "@ -Language CSharp -ReferencedAssemblies System.Windows.Forms

iex "[bluco.Program$id]::Main()"

My Bluetooth headset can now be switched on and off from the command line using powershell.

dzmanto
  • 151
4

Maybe Get-PnPDevice, Disable-PnPDevice and Enable-PnPDevice will do the trick for you. I havent been able to test it out though.

Example:

$DeviceName = "YourDevice"
$BTDevice =  Get-PnpDevice | Where-Object {$_.FriendlyName -eq $DeviceName -and $_.class -eq "Bluetooth"} 

Disable-PnpDevice -InstanceId $BTDevice.DeviceID -Confirm:$false

Enable-PnpDevice -InstanceId $BTDevice.DeviceID -Confirm:$false

Change the $DeviceName variable to the name of your BT device.

Get-PnPDevice Enable-PnPDevice Disable-PnPDevice

doenoe
  • 1,183
2

Using inspiration from https://github.com/stanleyguevara/win10-bluetooth-headphones I've revised that script so I can connect/disconnect multiple devices from a single shortcut click (or a keyboard shortcut bound to the shortcu.

disconnect shortcut properties dialog

My fork is here: https://github.com/nickboldt/win10-bluetooth-headphones

nickboldt
  • 121
2

https://github.com/m2jean/ToothTray is a project implementing connection/disconnection with bluetooth audio devices. From its README.md:

Instead of using Windows bluetooth API, Windows Core Audio API has to be used to connect bluetooth audio. Ultimately it is the driver that connect the bluetooth device to Windows' audio system, and we need to get an interface to the bluetooth audio driver.

After bluetooth audio devices are paired and the drivers are installed, they will appear in Windows as audio endpoint devices, which can be enumerated with EnumAudioEndpoints. Each endpoint has various properties. Their avalibility depends on the type of the endpoint. We can enumerate the properties programmatically, or view them in the details tab of a device from Device Manager.

One useful property is the state of an endpoint, which tells us whether a bluetooth audio device is connected. If it is connected, the state will be DEVICE_STATE_ACTIVE. Otherwise, it will be DEVICE_STATE_UNPLUGGED.

After getting the endpoints, we need to use the device topology API to get an interface to the driver. In Windows Driver Model (WDM), drivers expose the hardware and their functionality with a graph of kernel streaming (KS) components. By navgating through the topology, we can find the KS components that actually controls the connection of a bluetooth audio device.

And it turns out the the KS component we are looking for is always a KS filter directed connected to an audio endpoint (which might have been specified by related WDM audio driver development documents). We can get the filter by getting the connector of the filter from the connector of the endpoint. Then we can use the IKsControl interface of the filter to send KSPROPSETID_BtAudio property request to the driver, which is simlar to IO requests. By sending either KSPROPERTY_ONESHOT_RECONNECT or KSPROPERTY_ONESHOT_DISCONNECT, we ask the driver to connect or disconnect from the bluetooth audio device.

Detailed implementation can be seen in its code.

I forked it and made its core functionalities into CLI version for script use: https://github.com/shunf4/ToothTrayCli

Note the above only applies to bluetooth audio devices.

Shun Feng
  • 108
  • 1
  • 4
0

Edited based on OP response.

Looking at the underlying class, that item is not exposed for programmatic access.

Bluetooth​Device.​Close Method

When will Close/Disconnect be implemented? [Windows.Devices.Bluetooth.BluetoothDevice]

When will Close/Disconnect be implemented? Are you planning to have a disconnect? Dispose() doesn't close the connection

849 opened on Jan 9, 2019

There does not appear to be another API doc to say otherwise. So, it looks like you are stuck with SendKeys, AutoIT, etc., at least for now.

postanote
  • 5,136
0

Quite an old post, but I recently found a way to do this without any additional 3rd party tools.

TL;DR: In Windows 11, go to Settings > System > For Developers and enable Developer Mode, Device Portal, and Restrict to Loopback Connections Only. Disable Authentication. Go to http://localhost:50080/#Bluetooth to find the ID of your already-paired Bluetooth device; convert it to base64. Then the following command will work from the command line: curl -d "" http://localhost:50080/api/bt/connectdevice?deviceId={ID_in_base64}

Longer version: This solution takes advantage of the Windows Device Portal, which lets you administer your PC through web requests. Read more about what it can do here. Go to Settings > System > For Developers and enable Developer Mode, then enable Device Portal. This will trigger the installation of the Developer Mode Package, which can take a few minutes and may require a reboot. Once it’s enabled, enable Restrict to Loopback Connections Only. This is very important for security reasons; it means that you will only be able to access the Device Portal from your local PC. Otherwise, anyone on the same network will be able to read all the files on your PC. Once you’ve enabled that, you can safely disable Authentication. Alternatively, you can leave Authentication on, but keep in mind you will have to specify the username and password in plaintext in your web requests later.

Once that’s done, you will see the URL for the Device Portal, which should look like http://localhost:50080, though maybe the port number might be different. Go to that URL and you will see the Device Portal; select Bluetooth in the sidebar to see your Bluetooth Devices. Assuming your Bluetooth device is already paired, you will see it in the list even if it’s currently disconnected. Copy the full ID of your Bluetooth device and convert it to base64 using a website such as https://base64.guru/converter.

Finally, you can use the REST API to connect or disconnect your Bluetooth device. The easiest way is to use curl, which is installed by default in Windows these days. But you can also use the requests package in Python if you want to do something even more fancy. The URLs to connect or disconnect your device are:

http://localhost:50080/api/bt/connectdevice?deviceId={ID_in_base64}
http://localhost:50080/api/bt/disconnectdevice?deviceId={ID_in_base64}

To use curl, include the -d flag with empty quotes (which sets the Content Length to 0), then the URL, like so: curl -d "" {url}

-1

I have tested the below vbs script successfully in Windows 10. Here I am simulating the keystrokes using the SendKeys command. The number of {TAB}s will be different on your system to reach the bluetooth device name.

Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "ms-settings:bluetooth"
WScript.Sleep 2500 
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{TAB}"
WshShell.SendKeys "{ENTER}"
WScript.Sleep 500 ' 
WshShell.SendKeys "{TAB}"
WScript.Sleep 500 ' 
WshShell.SendKeys "{ENTER}"
phuclv
  • 30,396
  • 15
  • 136
  • 260