inthehand / 32feet

Personal Area Networking for .NET. Open source and professionally supported
https://inthehand.com/components/32feet/
MIT License
817 stars 206 forks source link

[QUESTION] Connect / Disconnect #132

Open steam3d opened 3 years ago

steam3d commented 3 years ago

image Can i connect / disconnect programmatically headphones? I found the way to connect using https://docs.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoothapis-bluetoothsetservicestate, but it takes a lot of time and device can't be disconnected even if i disable all services.

peterfoot commented 3 years ago

Historically BluetoothSetServiceState was used to enable/disable device manager entries for specific services e.g. virtual serial ports. I wouldn't recommend using it to toggle a headset. I will look to see if there is an alternative API which can be used to force a connection. It feels like this should be controlled via an Audio API by selecting a different default device but I'm not sure what APIs are available for this.

steam3d commented 3 years ago

I already tried Audio APIs, there is no way to change state of mmdevice.

There is a partial way to connect (but you can't disconnect device)

 using Windows.Devices.Bluetooth;
 async void ConnectBTUWP()
 {
   btdevice = await BluetoothDevice.FromIdAsync(BTId);
   rfcommResult = await btdevice.GetRfcommServicesAsync();
   rfcomm = rfcommResult.Services[0];

     if (btdevice.ConnectionStatus != BluetoothConnectionStatus.Connected)
       {
         // Create a socket and connect to the target
         var _socket = new StreamSocket();
         await _socket.ConnectAsync(
         rfcomm.ConnectionHostName,
         rfcomm.ConnectionServiceName,
         SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
       }    
 }

The only one dirty way to connect or disconnect device is disable all services or enable all services of device.

MarshalT commented 2 years ago

I'm having the same problem, I want to disconnect my computer from the Bluetooth connection or program it to disable it

PolarGoose commented 2 years ago

I'm struggling with finding a solution for this problem as well. I have created a https://github.com/PolarGoose/BluetoothDevicePairing console utility where I want to implement connection to devices the same way it is done via Window GUI. I have asked the same question on StackOverflow (How to connect to a paired audio Bluetooth device using Windows UWP API?) but haven't got any answer.

I have tried every solution I could find but none of them worked. Let me describe these solutions and why they don't work bellow.

Doesn't work: using BluetoothSetServiceState.

The fact that it doesn't work you can test yourself without any code

Doesn't work: If device is paired but not connected, unpair it first and then immediately pair again

In Windows.Devices.Enumeration namespace there are two methods PairAsync and UnpairAsync. If device hasn't been paired before and you call PairAsync Windows will pair and connect to the device. But if device is paired but not connected, PairAsync will just fail. But if we call UnpairAsync in this case and then after it finishes call PairAsync, Windows will pair and connect to the device. But it doesn't work reliably. In my case it only connected to my device as a "Music" but without "Voice".

Doesn't work: solution from @steam3d using StreamSocket.ConnectAsync

This just doesn't work. After you call _socket.ConnectAsync, Windows shows that device is just "Connected", but not "Connected as voice, music" and when the _socket object gets disposed the device gets disconnected immediately.

peterfoot commented 2 years ago

There isn't currently an API (Win32 or WinRT/UWP) to do this. As already stated BluetoothSetServiceState is not appropriate for this and should not be used. If the reason for disabling the headset is to play audio through a different endpoint you can do this by setting the AudioDevice of a MediaPlayer object. Use the DeviceInformation.FindAllAsync() method with a Windows.Media.Devices.MediaDevice.GetAudioRenderSelector() selector to enumerate all output devices, and then choose the one you want which isn't the headset. However apps can't disconnect a headset device.

PolarGoose commented 2 years ago

@peterfoot, Thank you for your reply. I'm just curious, do you know what happens when a user clicks "Connect" button in the Windows GUI? I mean what Bluetooth communication protocol Windows uses for that? For example do they open one of the Headphone's RFComm ports and write something to make headphones connect to the PC? Or maybe you know how Linux does it?

peterfoot commented 2 years ago

Windows connects via either Headset, Handsfree or A2DP profiles depending on the device. This causes a separate SCO channel to be opened for the audio (the main profile being an RFComm channel for control only). Trying to open a socket on one of those profiles yourself will not allow the normal Windows support for these to work, including the separate audio channel.

steam3d commented 2 years ago

Has anyone tried directly requesting information from Microsoft?

PolarGoose commented 2 years ago

@steam3d, I haven't tried to do that. Do you know how to do that?

peterfoot commented 2 years ago

The current place to ask technical questions is Microsoft Q&A - https://docs.microsoft.com/en-us/answers/topics/windows-api.html

PolarGoose commented 2 years ago

@peterfoot , Thank you for the link, I have created a topic: https://docs.microsoft.com/en-us/answers/questions/718643/connect-to-a-paired-bluetooth-device-from-c.html

PolarGoose commented 2 years ago

I think I have found something that works, even though it is still a workaround: Headphones have PnP devices related to them. Toggling states of these devices will make Windows connect to the headphones. Look at my answer on StackOverflow for more details: https://stackoverflow.com/a/71539568/7585517

shunf4 commented 1 year ago

Dunno if it helps, but here is a C++ project that successfully implemented connect/disconnect with (audio only) bluetooth devices, using winrt audio api: https://github.com/m2jean/ToothTray

And my CLI version fork: https://github.com/shunf4/ToothTray

PolarGoose commented 1 year ago

@shunf4, The link https://github.com/shunf4/ToothTray doesn't work.

I have also briefly tested the original m2jean/ToothTray. Unfortunately, it doesn't work correctly. When I disconnect my Headset it still has a status Connected in the Windows Bluetooth & Devices dialog image Or it disconnects only audio but leaves the microphone connected: image

shunf4 commented 1 year ago

The link https://github.com/shunf4/ToothTray doesn't work.

Sorry, it's https://github.com/shunf4/ToothTrayCli .

shunf4 commented 1 year ago

I have also briefly tested the original m2jean/ToothTray. Unfortunately, it doesn't work correctly. When I disconnect my Headset it still has a status Connected in the Windows Bluetooth & Devices dialog

Can you try if it works in Windows 10?

BTW seems like Sony's headsets really have quirks with them; IIRC I tried using one in Ubuntu Linux but failed; the headset just won't connect.

PolarGoose commented 1 year ago

@shunf4,

BTW seems like Sony's headsets really have quirks with them

It is not related to Sony, the same problem happens with Bose 700 and Jabra Evolve 75e, their status is left Connected or Connected mic when I try to disconnect them using ToothTray 1 2

Unfortunately, I don't have Windows 10 to test if ToothTray works there.

seanyu0 commented 8 months ago

@shunf4, The link https://github.com/shunf4/ToothTray doesn't work.

I have also briefly tested the original m2jean/ToothTray. Unfortunately, it doesn't work correctly. When I disconnect my Headset it still has a status Connected in the Windows Bluetooth & Devices dialog image Or it disconnects only audio but leaves the microphone connected: image

https://github.com/m2jean/ToothTray is the correct way to connect and disconnect bluetooth audio device. But in win11, this way can only enumerate headset service, lost headsfree service, so connect or disconnect doest not work properly. Just change IMMDeviceEnumerator::EnumAudioEndpoints parameter EDataFlow from eCapture to eAll, this problem will be fixed.

PolarGoose commented 8 months ago

@shunf4, The link https://github.com/shunf4/ToothTray doesn't work.

I have also briefly tested the original m2jean/ToothTray. Unfortunately, it doesn't work correctly. When I disconnect my Headset it still has a status Connected in the Windows Bluetooth & Devices dialog image Or it disconnects only audio but leaves the microphone connected: image

I have submitted a bug for this issue: https://github.com/m2jean/ToothTray/issues/10

PolarGoose commented 5 months ago

The approach implemented in the ToothTray utility works. It allows to connect and disconnect audio Bluetooth devices. Please read my comment here