Turn on/off Bluetooth radio/adapter from cmd/powershell in Windows 10
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
orexplorer.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...
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 Task
s. 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.
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:
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.
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.
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
.