42

Such a simple task, one would say, and I haven't found a satisfiable solution. What I've tried (with music playing through a Bluetooth-connected speaker to really know the state of the radio):

  • using devcon as admin: devcon disable USB\VID_8087&PID_07DC&REV_0001 (which is the HW ID of my Bluetooth adapter)... requires reboot to work...
  • using powershell as admin: Disable-NetAdapter "Bluetooth Network Connection 3" (which is translation of the name of my Bluetooth adapter)... it disables the PAN driver, but a Bluetooth speaker continues playing music...
  • using net as admin: net stop bthserv... doesn't actually turn off the radio (BT speaker continues playing music)
  • using .NET: The most relevant page on MSDN doesn't say a word about turning the adapter on/off.
  • using explorer: ms-settings:bluetooth or explorer.exe %LocalAppData%\Packages\windows.immersivecontrolpanel_cw5n1h2txyewy\LocalState\Indexed\Settings\cs-CZ\AAA_SettingsPagePCSystemBluetooth.settingcontent-ms... opens the Bluetooth settings panel, but I still have to click on the toggle

I can't believe Microsoft would be so ignorant to not provide such a command...

Martin Pecka
  • 892
  • 2
  • 11
  • 19

9 Answers9

47

This is challenging because of the necessary interoperation with WinRT, but it is possible in pure PowerShell:

[CmdletBinding()] Param (
    [Parameter(Mandatory=$true)][ValidateSet('Off', 'On')][string]$BluetoothStatus
)
If ((Get-Service bthserv).Status -eq 'Stopped') { Start-Service bthserv }
Add-Type -AssemblyName System.Runtime.WindowsRuntime
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
Function Await($WinRtTask, $ResultType) {
    $asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
    $netTask = $asTask.Invoke($null, @($WinRtTask))
    $netTask.Wait(-1) | Out-Null
    $netTask.Result
}
[Windows.Devices.Radios.Radio,Windows.System.Devices,ContentType=WindowsRuntime] | Out-Null
[Windows.Devices.Radios.RadioAccessStatus,Windows.System.Devices,ContentType=WindowsRuntime] | Out-Null
Await ([Windows.Devices.Radios.Radio]::RequestAccessAsync()) ([Windows.Devices.Radios.RadioAccessStatus]) | Out-Null
$radios = Await ([Windows.Devices.Radios.Radio]::GetRadiosAsync()) ([System.Collections.Generic.IReadOnlyList[Windows.Devices.Radios.Radio]])
$bluetooth = $radios | ? { $_.Kind -eq 'Bluetooth' }
[Windows.Devices.Radios.RadioState,Windows.System.Devices,ContentType=WindowsRuntime] | Out-Null
Await ($bluetooth.SetStateAsync($BluetoothStatus)) ([Windows.Devices.Radios.RadioAccessStatus]) | Out-Null

To use it, save it is a PS1 file, e.g. bluetooth.ps1. If you haven't already, follow the instructions in the Enabling Scripts section of the PowerShell tag wiki to enable the execution of scripts on your system. Then you can run it from a PowerShell prompt like this:

.\bluetooth.ps1 -BluetoothStatus On

To turn Bluetooth off, pass Off instead.

To run it from a batch file:

powershell -command .\bluetooth.ps1 -BluetoothStatus On

Caveat: If the Bluetooth Support Service is not running, the script attempts to start it because otherwise, WinRT will not see Bluetooth radios. Alas, the service cannot be started if the script is not running as administrator. To make that unnecessary, you can change the startup type of that service to automatic.

Now for some explanation. The first three lines establish the parameters the script takes. Before beginning in earnest, we make sure the Bluetooth Support Service is running and start it if not. We then load the System.Runtime.WindowsRuntime assembly so that we can use the WindowsRuntimeSystemExtensions.AsTask method to convert WinRT-style tasks (which .NET/PowerShell doesn't understand) to .NET Tasks. That particular method has a boatload of different parameter sets which seem to trip up PowerShell's overload resolution, so in the next line we get the specific one that takes only a resultful WinRT task. Then we define a function that we'll use several times to extract a result of the appropriate type from an asynchronous WinRT task. Following that function's declaration, we load two necessary types from WinRT metadata. The remainder of the script is pretty much just a PowerShell translation of the C# code you wrote in your answer; it uses the Radio WinRT class to find and configure the Bluetooth radio.

Ben N
  • 40,045
  • 17
  • 140
  • 181
  • 2
    You cannot call a method on a null-valued expression. At C:\Users\chx\AppData\Local\Microsoft\WindowsApps\bluetooth.ps1:18 char:1 + Await ($bluetooth.SetStateAsync($BluetoothStatus)) ([Windows.Devices. – chx Feb 09 '18 at 03:45
  • @chx Are you sure your machine has a Bluetooth adapter? If there is no Bluetooth radio, the third-to-last line will set `$bluetooth` to null, which will cause the second-to-last line to produce that error. – Ben N Feb 09 '18 at 03:46
  • I am 100% sure it's visible in the "Settings" app and I listen music wirelessly and both Device Manager and wmic also reports it. – chx Feb 09 '18 at 03:47
  • @chx Hmm, what do you get if you replace the last three lines with just `$radios`? That will show the list of radios that WinRT returned. – Ben N Feb 09 '18 at 03:49
  • @chx Ah, I found the problem. The Bluetooth Support Service must be running for the WinRT API to see Bluetooth radios. Somehow it gets triggered to start if the normal Settings app is used (possibly even without the user being an admin?), but I've added a line that makes sure it's running and starts it via the normal mechanism if not. This requires admin privileges, but it consistently works. Thanks for the note! – Ben N Feb 09 '18 at 04:16
  • $radios only has `WiFi Wi-Fi On`. This is in an Administrator PowerShell. `bthserv` is running. I am on 64 bit Windows 10, latest. – chx Feb 09 '18 at 06:02
  • @chx If you remove the ```| Out-Null``` on the line that includes `RequestAccessAsync`, what does it print? It should be `Allowed`. Also, does anything change for the better if you run the script from an x86 PowerShell process? – Ben N Feb 09 '18 at 13:57
  • 2
    Clever, it works! I just couldn't get to compile a desktop app using this API, but loading it dynamically in a script seems to be the way to go. Now, compare to `rfkill unblock bluetooth` on Linux :-D – Martin Pecka Feb 10 '18 at 00:30
  • @BenN yes, it says Allowed. – chx Feb 10 '18 at 00:41
  • @chx Hmm, what brand/model is your Bluetooth adapter? You can get that information from Device Manager. At least with other APIs, there seems to be some variance as to whether a manufacturer's devices will be listed. Also, there are non-WinRT ways of getting at this info, such as [a native API](https://msdn.microsoft.com/en-us/library/windows/desktop/aa362786(v=vs.85).aspx), but that function unhelpfully omits radios that are turned off. – Ben N Feb 10 '18 at 00:44
  • @BenN USB\VID_8087&PID_0A2B. . Intel Wireless Bluetooth 8265. – chx Feb 10 '18 at 00:49
  • @chx Strange, I have an extremely similar Intel Bluetooth device in my laptop. This is kind of a long shot, but does trying to update the driver for the device in Device Manager change anything? And though you've probably tried this already, does a reboot change anything? – Ben N Feb 11 '18 at 16:21
  • Thanks for the solution. It's working great on my Win 10.0.17134 .0 – Atiq Rahman May 06 '18 at 05:12
  • @BenN By chance, do you know how to reconnect a bluetooth hardware that has been disconnected using `Disable/Enable-NetAdapter`? https://superuser.com/questions/1444278/windows-10-disable-netadapter-disconnect-the-hardware-blutooth-from-the-compu – JinSnow Jun 03 '19 at 11:28
  • 2
    To adapt this script so that it itself checck if the bluetooth is on (or off) and the stop it (or start it) see https://superuser.com/q/1494499 – MagTun Oct 24 '19 at 06:12
  • Does this still work in Windows 11? – TylerDurden Jan 31 '23 at 12:02
  • @TylerDurden yes it does – Emil Vissing Apr 14 '23 at 05:36
10

After giving up to seek for ready-made solutions, I've found out that the Universal Windows Platform apps have access to the radio control API.

So I merged a sample radio control app with a commandline-controlled app, and the result is here: https://github.com/peci1/RadioControl . It's published on Windows Store.

In general, it isn't possible to just let the app run in background, so you need to first launch the GUI, and then you can call it from the commandline like

radiocontrol.exe 0 on
radiocontrol.exe Bluetooth off

The core features that this app needs are:

using Windows.Devices.Radios;
await Radio.RequestAccessAsync();
var radios = await Radio.GetRadiosAsync();
await radios[0].SetStateAsync(RadioState.On);

Then you also need to add the Radios capability to the app manifest:

<DeviceCapability Name="radios" />

And to allow control from commandline, you need to override method protected override async void OnActivated(IActivatedEventArgs args) in App.xaml.cs and do the processing in a branch like this:

switch (args.Kind)
        {
            case ActivationKind.CommandLineLaunch:
Martin Pecka
  • 892
  • 2
  • 11
  • 19
  • This answer better fits StackOverflow. – harrymc Feb 05 '18 at 11:40
  • @MartinPecka By chance, do you know how to reconnect Bluetooth hardware that has been disconnected using `Disable/Enable-NetAdapter`? https://superuser.com/questions/1444278/windows-10-disable-netadapter-disconnect-the-hardware-blutooth-from-the-compu – JinSnow Jun 03 '19 at 11:29
6

This Autohotkey script seems to do the trick. I am bothered by the Sleep there but it seems the window becomes active before it is ready to receive my keystrokes.

SendMode Input
Run, ms-settings:bluetooth
WinWaitActive, Settings
Sleep 300
Send,{Tab}{Space}
WinClose, A

Crossposted to https://stackoverflow.com/q/48700268/308851 to figure out what that Sleep 300 could be replaced with.

chx
  • 3,633
  • 6
  • 30
  • 68
  • This could also be a solution in most cases. I needed to also add a short sleep after sending the keystrokes. And this solution doesn't work if the screen is locked. The Universal Windows app I posted above does work even with locked screen. – Martin Pecka Feb 09 '18 at 15:00
3

Here's a partial solution: wmic path Win32_PNPEntity where "caption like '%bluetooth%' AND DeviceID like 'USB\\%'" call disable certainly makes the Bluetooth On/Off switch disappear and Device Manager reports This device is disabled. (Code 22). Whether that's enough to kill the radio, I am not sure.

I am unable to find documentation on what arguments call accepts. I think there should be some sort of rfkill here.

chx
  • 3,633
  • 6
  • 30
  • 68
  • I'm sorry, doesn't work for me as you describe it. For me, the Bluetooth playback stops, but all the bluetooth switches in control panel remain on. And calling with `enable` doesn't resume the Bluetooth playback. The documentation on the PNPEntity class is here: https://msdn.microsoft.com/en-us/library/aa394353(v=vs.85).aspx , but no other useful methods seem to be there... – Martin Pecka Feb 04 '18 at 17:43
3

How about using pnputil?

pnputil /disable-device "<device instance ID>"
pnputil /enable-device "<device instance ID>"

Does not require reboot.

Beelzebub
  • 31
  • 1
1

Executing the following command from Command Prompt (as administrator) did the trick:

netsh interface set interface name="Bluetooth Network Connection" admin=disabled

I had previously used the same command to disable my Wi-Fi adapter, and found that it also worked with the other adapters in: Control Panel > Network and Internet > Network Connections. Just change the "name" argument to the name of your adapter. You can also enable it again by changing the last argument to admin=enabled.

Greenonline
  • 2,235
  • 11
  • 24
  • 30
Micah Vertal
  • 121
  • 3
  • 2
    This also seems to just disable the PAN drivers, but doesn't turn off the radio device itself (in control panel, Bluetooth is still being reported as "on"). – Martin Pecka Jul 05 '17 at 23:57
  • 1
    Verified now by the bluetooth repro test, doesn't turn off bluetooth. – Martin Pecka Jul 06 '17 at 21:43
  • I'm sorry @peci1 , I misunderstood your question. I had previously been looking at questions relating to _disabling_ Bluetooth and interpreted your question as the same. So your goal is to _turn off_ (cut power to) the Bluetooth device? I will research a bit and let you know if I find anything. – Micah Vertal Jul 07 '17 at 02:33
  • @peci1 would [this link](https://superuser.com/a/720824/620385) be helpful at all? – Micah Vertal Jul 16 '17 at 22:09
  • No, it does not. It's about USB, not Bluetooth devices. And the linked Device Management Cmdlet doesn't work, either. – Martin Pecka Aug 30 '17 at 01:18
1

You can use DevManView to enable/disable a device from the command line using this syntax:

devmanview /disable <Device Name>   
devmanview /enable <Device Name>    

It also allows the use of wildcards with the /use_wildcard argument.

undo
  • 5,961
  • 8
  • 41
  • 61
  • Devmanview is indeed a useful tool, but it doesn't solve my problem. The problem is that there doesn't seem to be a device which you could disable in device manager to turn off the bluetooth radio. – Martin Pecka Feb 10 '18 at 00:03
  • @MartinPecka Do you not see any of these? https://i.stack.imgur.com/UrOuc.png If not, you might want to try and update drivers... – undo Feb 10 '18 at 08:52
  • I do see the Bluetooth device, but disabling it doesn't stop the playback - probably because the system needs to be restarted for it to really become disabled (as stated in the original question). – Martin Pecka Feb 10 '18 at 17:19
1

I made a batch script that checks a reg key value which would only be in place if your bluetooth radio was enabled. It then calls Ben N's powershell script above with the correct parameter to switch its current state. Turns it into a toggle shortcut, rather than having to use on and off separately.

@echo off
FOR /F "skip=2 tokens=2,*" %%A IN ('reg.exe query "HKLM\SYSTEM\CurrentControlSet\Services\bthserv\Parameters\BluetoothControlPanelTasks" /v "state"') DO set "VAR=%%B"

if %VAR%==0x1 (goto ON)

Powershell.exe -executionpolicy remotesigned -windowstyle hidden -File ".\bluetooth.ps1" -BluetoothStatus On
goto end

:ON
Powershell.exe -executionpolicy remotesigned -windowstyle hidden -File ".\bluetooth.ps1" -BluetoothStatus Off

:end
007craft
  • 31
  • 2
0

Powershell has cmdlets for doing exactly what you're seeking:

https://gallery.technet.microsoft.com/PowerShell-Device-60d73bb0

Use Get-Device to get a list of the devices. You can pipe that through where-object to pick your device and then pipe that to Disable-Device or Enable-Device.

Detailed Instructions...

Download and unzip the file.

Start a powershell as admin.

Change execution policy if necessary:

Set-ExecutionPolicy Unrestricted

Import the module:

Import-Module .\DeviceManagement.psd1

If you need to narrow down the list of devices you can use a -like compare:

Get-Device | Where-Object {$_.Name -like '*bluetooth*'}

If you know the exact name you can use an -eq (equals) compare:

Get-Device | Where-Object {$_.Name -eq 'Martin's Bluetooth Adapter'} | Disable-Device -Confirm:$False

Wipe hands on pants.

apocalysque
  • 1,218
  • 7
  • 12
  • 2
    This will disable the USB device instead of just the radio. `wmic` can do this too, at least I think so. – chx Feb 09 '18 at 03:39
  • The question didn't ask how to disable to radio but not the adapter. Why the distinction? If you can give me a good reason I'll consider digging for you, but I answered the question that was asked "What I'm really interested in is really a way to disable/enable the Bluetooth adapter. – Martin Pecka Feb 5 at 9:31" – apocalysque Feb 09 '18 at 04:04
  • I'm sorry, what's the difference between adapter and radio? I consider these two terms equivalent. There's a ***load of software devices this HW thing provides, and you're right this question is not about them. – Martin Pecka Feb 10 '18 at 00:06
  • Moreover, both this and the 2012 PS script from Ricardo Mendes do not work at my Win 10 1709 64bit, as is mentioned in their comments. Both end up with error `Disable-Device : Error calling SetupDiCallClassInstaller`. I run them as admin and with unrestricted execution policy. – Martin Pecka Feb 10 '18 at 00:22