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