28

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...

  • How are you playing the music? – harrymc Feb 4 '18 at 20:33
  • Using Winamp :) – Martin Pecka Feb 4 '18 at 21:04
  • Does killing Winamp stop the music? – harrymc Feb 4 '18 at 21:57
  • Of course, why it wouldn't? But the question is not about stopping music playback, it is about control of the BT radio. – Martin Pecka Feb 4 '18 at 23:20
  • 1
    @harrymc Read the question, please. Music playback is just a way how I detect if the Bluetooth connection is alive or not. What I'm really interested in is really a way to disable/enable the Bluetooth adapter. – Martin Pecka Feb 5 '18 at 9:31
20
+100

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.

  • 1
    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 9 '18 at 3: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 9 '18 at 3: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 9 '18 at 3: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 9 '18 at 3:49
  • 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 0:30
5

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 answer better fits StackOverflow. – harrymc Feb 5 '18 at 11:40
  • @MartinPecka By chance, do you know how to reconnect Bluetooth hardware that has been disconnected using Disable/Enable-NetAdapter? superuser.com/questions/1444278/… – JinSnow Jun 3 at 11:29
3

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.

  • 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 9 '18 at 15:00
2

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.

  • 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: msdn.microsoft.com/en-us/library/aa394353(v=vs.85).aspx , but no other useful methods seem to be there... – Martin Pecka Feb 4 '18 at 17:43
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.

  • 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 0:03
  • @MartinPecka Do you not see any of these? i.stack.imgur.com/UrOuc.png If not, you might want to try and update drivers... – rahuldottech Feb 10 '18 at 8: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
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.

  • This will disable the USB device instead of just the radio. wmic can do this too, at least I think so. – chx Feb 9 '18 at 3: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 9 '18 at 4: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 0: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 0:22
0

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
-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.

  • 1
    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 5 '17 at 23:57
  • Verified now by the bluetooth repro test, doesn't turn off bluetooth. – Martin Pecka Jul 6 '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 7 '17 at 2:33
  • @peci1 would this link 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 1:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.