# 不知道耳機名稱。列出所有藍牙設備
Get-PnpDevice -class system | where service -eq BthHFEnum
# 藍牙耳機的電量
$name = 'sound'
$a = Get-PnpDevice -class system -FriendlyName "*$name*" | Get-PnpDeviceProperty  -KeyName '{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2'
$a.Data
# 配對的時間(第一次配對or刪除配對資料、重新配對)
$name = 'sound'
$a = Get-PnpDevice -class system -FriendlyName "*$name*" | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_InstallDate'
$a.Data
# 連線到電腦的時間
$name = 'sound'
$a = Get-PnpDevice -class system -FriendlyName "*$name*" | Get-PnpDeviceProperty -KeyName 'DEVPKEY_Device_LastArrivalDate'
$a.Data
# 電腦藍牙狀態(開啟or關閉)。不是藍牙耳機有沒有連線
# DEVPKEY_Device_IsPresent
Win10以上適用
藍牙有兩種:傳統藍牙、低功耗藍牙
這是用來查看傳統藍牙藍牙耳機的電量
之前使用python的bleak模組,想要掃描自己的藍牙耳機
但是,沒有找到。
倒是有找到別人的LE_WF-1000XM4、Xiaomi Band 8 Active 2D11、Xiaomi Smart Band 8 2ADC
如今赫然發現
Bleak is an acronym for Bluetooth Low Energy platform Agnostic Klient.
$name的值為藍牙耳機名稱
Add-Type -AssemblyName System.Runtime.WindowsRuntime
名稱空間的其中一個類別,以使用名稱空間的所有類別
[類別, 名稱空間, ContentType=WindowsRuntime][Windows.Devices.Bluetooth.BluetoothDevice, Windows.Devices.Bluetooth, ContentType=WindowsRuntime] > $null
System.__ComObject,沒有可用的屬性、方法await  (方法名)  (參數) (參數) (參數)方法名、參數圍起來方法名、參數是變數,可以不用圍起來空格分隔。不要用逗號。$name = 'sound'
Add-Type -AssemblyName System.Runtime.WindowsRuntime
# learn from https://fleexlab.blogspot.com/2018/02/using-winrts-iasyncoperation-in.html
# leran from https://superuser.com/questions/1168551/turn-on-off-bluetooth-radio-adapter-from-cmd-powershell-in-windows-10
Function await($method) {
    $IAsyncT = $null
    if ($method.ReturnType){
        $IAsyncT = $method.ReturnType
        $WinRtTask = $method.Invoke($args[0], $args[1..100])
        <#
        $socket = [Windows.Networking.Sockets.StreamSocket]::new()
        $socket.InputStream is System.__ComObject
        can't use $socket.InputStream.ReadAsync method
        $readMethod = [Windows.Storage.Streams.IInputStream].GetDeclaredMethod('ReadAsync')
        use await function
            error
                await $readMethod $socket.InputStream $buffer [uint32]1KB [Windows.Storage.Streams.InputStreamOptions]::Partial
            ok
                await $readMethod $socket.InputStream $buffer ([uint32]1KB) ([Windows.Storage.Streams.InputStreamOptions]::Partial)
                    or
                $a = [uint32]1KB
                $b = [Windows.Storage.Streams.InputStreamOptions]::Partial
                await $readMethod $socket.InputStream $buffer $a $b
        in await function
            $method
                $readMethod
            $args[0]
                $socket.InputStream
            $args[1..100]
                $buffer, $a, $b
        #>
    }elseif($method.OverloadDefinitions[0] -match "(Windows.Foundation.IAsync.*) $($method.Name)"){
        $IAsyncT = [type]$Matches[1]
        $WinRtTask = $method.Invoke($args)
        <#
        use await function
            error
                await [Windows.Storage.StorageFile]::GetFileFromPathAsync('C:\1.txt')
                await [Windows.Storage.StorageFile]::GetFileFromPathAsync 'C:\1.txt'
            ok
                await ([Windows.Storage.StorageFile]::GetFileFromPathAsync) 'C:\1.txt'
                    or
                $a = [Windows.Storage.StorageFile]::GetFileFromPathAsync
                await $a 'C:\1.txt'
        in await function
            $method
                [Windows.Storage.StorageFile]::GetFileFromPathAsync
            $method.OverloadDefinitions[0]
                  static Windows.Foundation.IAsyncOperation[Windows.Storage.StorageFile] GetFileFromPathAsync(string path)
            $args
                @('C:\1.txt')
        #>
    }else{
        throw 'method error'
    }
    $asTask = [System.WindowsRuntimeSystemExtensions].GetMethods() | where {
        $_.Name -eq 'AsTask' -and
        $IAsyncT.Name -eq $_.GetParameters().ParameterType.name
    }
    $ResultType = $IAsyncT.GenericTypeArguments
    if ($ResultType.Count -gt 0){
        $asTask = $asTask.MakeGenericMethod($ResultType)
    }
    $netTask = $asTask.Invoke($null, @($WinRtTask))
    $netTask.Wait(-1) | Out-Null
    if ($IAsyncT.Name -like 'IAsyncOperation*'){
        return $netTask.Result
    }
}
[Windows.Devices.Radios.Radio, Windows.System.Devices, ContentType=WindowsRuntime] > $null
[Windows.Devices.Bluetooth.BluetoothDevice, Windows.Devices.Bluetooth, ContentType=WindowsRuntime] > $null
[Windows.Devices.Enumeration.DeviceInformation, Windows.Devices.Enumeration, ContentType=WindowsRuntime] > $null
[Windows.Storage.Streams.Buffer, Windows.Storage.Streams, ContentType=WindowsRuntime] > $null
<#
# Turn on Bluetooth adapter
$r = await ([Windows.Devices.Radios.Radio]::RequestAccessAsync)
if ($r -eq 'Allowed'){
    $radios = await ([Windows.Devices.Radios.Radio]::GetRadiosAsync)
    $bluetooth = $radios | where Kind -eq 'Bluetooth'
    if ($bluetooth.State -ne 'On'){
        # on   off
        await $bluetooth.SetStateAsync 'on' > $null
    }
}
#>
$q = [Windows.Devices.Bluetooth.BluetoothDevice]::GetDeviceSelectorFromPairingState($true)
$arr = await ([Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync) $q
$deviceId = $arr | where Name -like "*$name*" | select -expand Id
$BluetoothDevice = await ([Windows.Devices.Bluetooth.BluetoothDevice]::FromIdAsync) $deviceId
<#
$deviceId = Get-PnpDevice -FriendlyName "*$name*" -class system | Get-PnpDeviceProperty -KeyName '{3B2CE006-5E61-4FDE-BAB8-9B8AAC9B26DF} 8' | select  -ExpandProperty data
$BluetoothDevice = await ([Windows.Devices.Bluetooth.BluetoothDevice]::FromIdAsync) $deviceId
# $BluetoothDevice.ConnectionStatus
#>
$r = await $BluetoothDevice.GetRfcommServicesAsync
# HeadsetServiceClass_UUID {00001108-0000-1000-8000-00805F9B34FB}
$service = $r.Services | where ConnectionServiceName -like '*{00001108*'
$socket = [Windows.Networking.Sockets.StreamSocket]::new()
while($true){
    try{
        await $socket.ConnectAsync $service.ConnectionHostName $service.ConnectionServiceName
    }catch{
        continue
    }
    break
}
$buffer = [Windows.Storage.Streams.Buffer]::new(1KB)
$readMethod = [Windows.Storage.Streams.IInputStream].GetDeclaredMethod('ReadAsync')
$output = ''
while ($output -notlike '*IPHONEACCEV*'){
    $result = await $readMethod $socket.InputStream $buffer ([uint32]1KB) ([Windows.Storage.Streams.InputStreamOptions]::Partial)
    $reader = [Windows.Storage.Streams.DataReader]::FromBuffer($result)
    $output = $reader.ReadString($result.Length)
    echo $output
}
<#
AT+IPHONEACCEV=2,1,7,2,0
AT+IPHONEACCEV=(Number of key/value pairs),key1,val1,key2,val2,...
key
    1 = Battery Level
    2 = Dock State
value
    Battery Level: string value between '0' and '9'
    Dock State: 0 = undocked, 1 = docked
#>
$arr = ($output -split '=')[1] -split ','
$n, $arr = $arr
while ($null -ne $arr){
    $a, $b, $arr = $arr
    if ($a -eq '1'){
        echo ""
        echo "battery:$(1+$b)0%"
        break
    }
}
$socket.Dispose()
Pause
TheWeirdDev/Bluetooth_Headset_Battery_Level · GitHub
這個連結有提到用Google Fast Pair 去查看左耳、右耳的電量
放回充電盒、接觸不良、沒有充到電
如果那一耳是主耳機,放回充電盒、有在充電的話,會中斷藍牙連線
Turn on/off Bluetooth radio/adapter from cmd/powershell in Windows 10 - Super User
windows runtime - How can I handle WinRT IInputStream in Powershell? - Stack Overflow