[C#/WinRT] WPFでBluetooth v4(BLE)機器とペアリングする

# BLE関連記事

- [[C#/WinRT]Bluetooth v4(BLE)機器と通信する（BLE基礎メモ）](https://qiita.com/tera1707/items/e04d86a68a9f96e02fe9)
- [[C#/WinRT]Bluetooth v4(BLE)機器と通信する（C#で実装メモ）](https://qiita.com/tera1707/items/5505ed22b5ddf93795f4)
- [[C#/WinRT] WPFでBluetooth v4(BLE)機器とペアリングする](https://qiita.com/tera1707/items/1c066934774e0dd74cac)

# やりたいこと
BLE機器をペアリングするときに、Windowsの設定画面でペアリングするのではなく、プログラム中にペアリング処理をいれたい。

# やり方
[こちら](https://qiita.com/tera1707/items/5505ed22b5ddf93795f4#%E8%A6%8B%E3%81%A4%E3%81%8B%E3%81%A3%E3%81%9F%E7%92%B0%E5%A2%83%E3%82%BB%E3%83%B3%E3%82%B5%E3%81%AE%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E6%83%85%E5%A0%B1%E3%82%92%E4%BF%9D%E5%AD%98)でやったようにしてDeviceInformationを取得して、それをもとに、ペアリングを行う。サンプルは下記。

```ペアリング.cs
private async void DoPairing(DeviceInformation devInfo)
{
    // devInfoが、watcherで取得したデバイス情報。
    if (devInfo == null) return;

    if (devInfo.Pairing.IsPaired == false)
    {
        DeviceInformationCustomPairing customPairing = devInfo.Pairing.Custom;
        customPairing.PairingRequested += PairingRequestedHandler;
        DevicePairingResult result = await customPairing.PairAsync(DevicePairingKinds.ConfirmOnly, DevicePairingProtectionLevel.Default);
        customPairing.PairingRequested -= PairingRequestedHandler;
        Debug.WriteLine($"ペアリング結果：{result.Status}");
    }
    else
    {
        // すでにペアリング済み
    }
}
```

```ペアリング解除.cs
private async void DoUnpairing(DeviceInformation devInfo)
{
    if (devInfo == null) return;

    if (devInfo.Pairing.IsPaired == true)
    {
        var result = await devInfo.Pairing.UnpairAsync();
        Debug.WriteLine($"ペアリング解除結果：{result.Status}");
    }
    else
    {
        // すでにペアリング解除済み
    }
}
```

```ペアリング要求時のハンドラ.cs
private static void PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
    switch (args.PairingKind)
    {
        case DevicePairingKinds.ConfirmOnly:
            args.Accept();
            break;
    }
}
```

# ペアリング要求時のハンドラについて
下記に、上のコードの基にしたマイクロソフトのペアリングのサンプルがある。

■コードのサンプル
https://github.com/microsoft/Windows-universal-samples/blob/master/Samples/DeviceEnumerationAndPairing/cs/Scenario9_CustomPairDevice.xaml.cs

■DevicePairingKindsのenumについて
https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicepairingkinds

このハンドラ(ここではPairingRequestedHandler)では、アプリのユーザーに「本当にペアリングしてよいですか？」や「パスワードを入力してください」、「今表示しているPINコードを、相手のデバイスに入力してください」などの**「確認」**の制御を行う。

上のサンプルでは、簡単のために**何も確認せずにペアリングを実行するようにした**が、MSのサンプルでは、下記のように各種確認ダイアログの表示を行っていた。

```Scenario9_CustomPairDevice.xaml.cs
private async void PairingRequestedHandler(
    DeviceInformationCustomPairing sender,
    DevicePairingRequestedEventArgs args)
{
    switch (args.PairingKind)
    {
        case DevicePairingKinds.ConfirmOnly:
            // Windows itself will pop the confirmation dialog as part of "consent" if this is running on Desktop or Mobile
            // If this is an App for 'Windows IoT Core' where there is no Windows Consent UX, you may want to provide your own confirmation.
            args.Accept();
            break;

        case DevicePairingKinds.DisplayPin:
            // We just show the PIN on this side. The ceremony is actually completed when the user enters the PIN
            // on the target device. We automatically accept here since we can't really "cancel" the operation
            // from this side.
            args.Accept();

            // No need for a deferral since we don't need any decision from the user
            await rootPage.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                ShowPairingPanel(
                    "Please enter this PIN on the device you are pairing with: " + args.Pin,
                    args.PairingKind);

            });
            break;

        case DevicePairingKinds.ProvidePin:
            // A PIN may be shown on the target device and the user needs to enter the matching PIN on
            // this Windows device. Get a deferral so we can perform the async request to the user.
            var collectPinDeferral = args.GetDeferral();

            await rootPage.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                string pin = await GetPinFromUserAsync();
                if (!string.IsNullOrEmpty(pin))
                {
                    args.Accept(pin);
                }

                collectPinDeferral.Complete();
            });
            break;

        case DevicePairingKinds.ProvidePasswordCredential:
            var collectCredentialDeferral = args.GetDeferral();
            await rootPage.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                var credential = await GetPasswordCredentialFromUserAsync();
                if (credential != null)
                {
                    args.AcceptWithPasswordCredential(credential);
                }
                collectCredentialDeferral.Complete();
            });
            break;

        case DevicePairingKinds.ConfirmPinMatch:
            // We show the PIN here and the user responds with whether the PIN matches what they see
            // on the target device. Response comes back and we set it on the PinComparePairingRequestedData
            // then complete the deferral.
            var displayMessageDeferral = args.GetDeferral();

            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
            {
                bool accept = await GetUserConfirmationAsync(args.Pin);
                if (accept)
                {
                    args.Accept();
                }

                displayMessageDeferral.Complete();
            });
            break;
    }
}
```

|enum名|意味|
|:--|:--|
|DevicePairingKinds.ConfirmOnly|ペアリングするか確認だけ行う。今回のサンプルでは確認ダイアログを出したりはしていない。確認ダイアログ必要な場合は、`Accept()`の前にダイアログを出す処理を書いて、OKを押したときだけAccept()する、などしてやる|
|DevicePairingKinds.ConfirmPinMatch|ユーザーにPINを表示して、「表示中のPINは相手のデバイスに表示されているPINと一致していますか？」という確認をさせる|
|DevicePairingKinds.DisplayPin|ユーザーにPINを表示して、「表示中のPINを相手のデバイスに入力してください」という指示をする|
|DevicePairingKinds.None|ペアリングに対応していない|
|DevicePairingKinds.ProvidePasswordCredential|ユーザーにパスワードを入力させる|
|DevicePairingKinds.ProvidePin|ユーザーにPINを入力させる|

※詳細は、[こちら](https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicepairingkinds#fields)参照。