Skip to content

Commit

Permalink
Add ability to provide PIN code for pairing. Improve pairing with dev…
Browse files Browse the repository at this point in the history
…ices which ask confirmation if PIN code matches.
  • Loading branch information
PolarGoose committed Dec 18, 2020
1 parent f17776a commit c006e9f
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 20 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ Windows 10 1809 (10.0.17763) or higher<br>
* Pair and connect to a device using its mac address: `BluetoothDevicePairing.exe pair --mac 12:34:56:78:9A:BC`
* Pair and connect to a device using its name: `BluetoothDevicePairing.exe pair --name "name of device"`
* Pair and connect to a device using its name/mac and device type: `BluetoothDevicePairing.exe pair --name "name of device" --type BluetoothLE`
* Pair and connect to a device using its name/mac and pin code: `BluetoothDevicePairing.exe pair --mac 12:34:56:78:9A:BC --pin 1234`
* Unpair a device using its mac address: `BluetoothDevicePairing.exe unpair --mac 12:34:56:78:9A:BC`
* Unpair a device using its name: `BluetoothDevicePairing.exe unpair --name "name of device"`
* Unpair a device using its name/mac and device type: `BluetoothDevicePairing.exe unpair --mac 12:34:56:78:9A:BC --type Bluetooth`

# Tips and tricks
* Bluetooth LE devices use mac address randomisation, therefore it is not reliable to pair them using mac address. Use pairing by name instead.
* Some devices advertize itself as Bluetooth and BluetoothLE simultaneously while having the same mac and name. To work with such devices explicitly specify to which type of device you want to connect using `--type` parameter.
* Some device require pin code to be paired, use `--pin` parameter to provide PIN code. By default this programm will try to use `0000` as a pin code.

# Build
* Use `Visual Studio 2019` to open the solution file and work with the code
Expand Down
6 changes: 3 additions & 3 deletions Src/Bluetooth/Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace BluetoothDevicePairing.Bluetooth
internal enum DeviceType
{
Bluetooth,
BluetoothLe
BluetoothLE
}

internal sealed class Device
Expand All @@ -35,7 +35,7 @@ public bool IsConnected
case DeviceType.Bluetooth:
var b = BluetoothDevice.FromIdAsync(Info.Id).GetAwaiter().GetResult();
return b.ConnectionStatus == BluetoothConnectionStatus.Connected;
case DeviceType.BluetoothLe:
case DeviceType.BluetoothLE:
var ble = BluetoothLEDevice.FromIdAsync(Info.Id).GetAwaiter().GetResult();
return ble.ConnectionStatus == BluetoothConnectionStatus.Connected;
}
Expand All @@ -60,7 +60,7 @@ private static DeviceType GetDeviceType(DeviceInformation device)
case "Bluetooth":
return DeviceType.Bluetooth;
case "BluetoothLE":
return DeviceType.BluetoothLe;
return DeviceType.BluetoothLE;
default:
throw new Exception($"Wrong device type '{type}' extracted from '{device.Id}'");
}
Expand Down
46 changes: 41 additions & 5 deletions Src/Bluetooth/DevicePairer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace BluetoothDevicePairing.Bluetooth
{
internal sealed class DevicePairer
{
public static void PairDevice(Device device)
public static void PairDevice(Device device, string pin)
{
Console.WriteLine($"Request to pair device \"{device}\"");

Expand All @@ -21,7 +21,7 @@ public static void PairDevice(Device device)
}

Console.WriteLine("Start pairing");
Pair(device.Info);
Pair(device.Info, pin);
Console.WriteLine("Device has been successfully paired");
}

Expand All @@ -47,16 +47,52 @@ private static void Unpair(DeviceInformation device)
}
}

private static void Pair(DeviceInformation device)
private static void Pair(DeviceInformation device, string pin)
{
device.Pairing.Custom.PairingRequested += (sender, args) => { args.Accept(); };
device.Pairing.Custom.PairingRequested += (s, a) => PairingRequestedHandler(s, a, pin);

var res = device.Pairing.Custom.PairAsync(DevicePairingKinds.ConfirmOnly, DevicePairingProtectionLevel.None)
// DeviceInformation.Pairing.PairAsync function doesn't work for non UWP applications. Thus, DeviceInformation.Pairing.Custom.PairAsync is used.
// https://stackoverflow.com/questions/45191412/deviceinformation-pairasync-not-working-in-wpf

// DevicePairingKinds.DisplayPin option conflicts with DevicePairingKinds.ProvidePin: I used "Bluetooth Module HC 05" to test pairing with PIN code.
// This device requires pin code "1234" to be paired. When both DevicePairingKinds.DisplayPin and DevicePairingKinds.ProvidePin flags were used in PairAsync function,
// the PairingRequestedHandler was called with PairingKind equal to DevicePairingKinds.DisplayPin instead of DevicePairingKinds.ProvidePin, which made pairing fail.
// Therefore, I decided not to use DevicePairingKinds.DisplayPin flag.

var res = device.Pairing.Custom
.PairAsync(DevicePairingKinds.ConfirmOnly | DevicePairingKinds.ProvidePin | DevicePairingKinds.ConfirmPinMatch, DevicePairingProtectionLevel.None)
.GetAwaiter().GetResult().Status;
if (res != DevicePairingResultStatus.Paired)
{
throw new Exception($"Failed to pair device. Status = {res}");
}
}

private static void PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args, string pin)
{
switch (args.PairingKind)
{
case DevicePairingKinds.ConfirmOnly:
Console.WriteLine("Pairing mode: ConfirmOnly");
args.Accept();
return;

case DevicePairingKinds.ProvidePin:
Console.WriteLine("Pairing mode: ProvidePin");
Console.WriteLine($"Pin is requested by the device. Using '{pin}' as a pin code");
args.Accept(pin);
return;

case DevicePairingKinds.ConfirmPinMatch:
Console.WriteLine("Pairing mode: ConfirmPinMatch");
Console.WriteLine($"The device's pin code: '{args.Pin}'");
Console.WriteLine("Waiting for the target device to accept the pairing (you probably need to follow the instructions on the target device's screen)");
args.Accept();
return;
}

Console.WriteLine($"Unexpected pairing type: {args.PairingKind}");
throw new Exception();
}
}
}
2 changes: 1 addition & 1 deletion Src/Command/DiscoverDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private static void PrintDevice(Device d)

private static string GetType(Device d)
{
return d.Type == Bluetooth.DeviceType.BluetoothLe ? "LE" : "";
return d.Type == Bluetooth.DeviceType.BluetoothLE ? "LE" : "";
}

private static string GetPairedStatus(Device d)
Expand Down
24 changes: 14 additions & 10 deletions Src/Command/PairDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ internal sealed class PairDeviceOptions : PairAndUnpairDeviceOptions
[Option("discovery-time", Default = 10,
HelpText = "how long to search for devices. Units: seconds")]
public int DiscoveryTime { get; set; }

[Option("pin", Default = "0000",
HelpText = "pin code to provide to a device if it requires it for pairing")]
public string Pin { get; set; }
}

internal sealed class PairDevice
Expand All @@ -20,25 +24,25 @@ public static void Execute(PairDeviceOptions opts)
{
if (!string.IsNullOrEmpty(opts.Mac))
{
PairWithMac(new MacAddress(opts.Mac), opts.DiscoveryTime, opts.Type);
PairWithMac(new MacAddress(opts.Mac), opts.DiscoveryTime, opts.Type, opts.Pin);
}
else if (!string.IsNullOrEmpty(opts.DeviceName))
{
PairWithName(opts.DeviceName, opts.DiscoveryTime, opts.Type);
PairWithName(opts.DeviceName, opts.DiscoveryTime, opts.Type, opts.Pin);
}
else
{
throw new Exception("Mac or device name must be specified");
}
}

private static void PairWithMac(MacAddress mac, int discoveryTime, Utils.DeviceType deviceType)
private static void PairWithMac(MacAddress mac, int discoveryTime, Utils.DeviceType deviceType, string pin)
{
var devices = DeviceFinder.FindDevicesByMac(DeviceDiscoverer.DiscoverBluetoothDevices(discoveryTime), mac, deviceType);

if (devices.Count == 1)
{
DevicePairer.PairDevice(devices[0]);
DevicePairer.PairDevice(devices[0], pin);
return;
}

Expand All @@ -52,26 +56,26 @@ private static void PairWithMac(MacAddress mac, int discoveryTime, Utils.DeviceT
$"{devices.Count} devices with the mac '{mac}' found. Don't know which one to choose");
}

private static void PairWithName(string name, int discoveryTime, Utils.DeviceType deviceType)
private static void PairWithName(string name, int discoveryTime, Utils.DeviceType deviceType, string pin)
{
var devices = DeviceFinder.FindDevicesByName(DeviceDiscoverer.DiscoverBluetoothDevices(discoveryTime), name, deviceType);
if (devices.Count == 1)
{
DevicePairer.PairDevice(devices[0]);
DevicePairer.PairDevice(devices[0], pin);
return;
}

if (devices.Count == 2 && devices[0].Type == Bluetooth.DeviceType.BluetoothLe && devices[1].Type == Bluetooth.DeviceType.BluetoothLe)
if (devices.Count == 2 && devices[0].Type == Bluetooth.DeviceType.BluetoothLE && devices[1].Type == Bluetooth.DeviceType.BluetoothLE)
{
HandleSituation_2_BluetoothLe_devices_with_the_same_name_found(devices[0], devices[1]);
HandleSituation_2_BluetoothLe_devices_with_the_same_name_found(devices[0], devices[1], pin);
return;
}

throw new Exception($"{devices.Count} devices with the name '{name}' found. Don't know which one to choose");
}

private static void HandleSituation_2_BluetoothLe_devices_with_the_same_name_found(Device device1,
Device device2)
Device device2, string pin)
{
// BLuetooth LE devices use mac randomization, which can lead to the situation when
// the user already have the device paired but with different mac address.
Expand All @@ -85,7 +89,7 @@ private static void HandleSituation_2_BluetoothLe_devices_with_the_same_name_fou
Console.WriteLine($"2 devices with the same name found: \"{oldDevice}\" (paired) and \"{newDevice}\"");
Console.WriteLine("Assume that the device changed its mac address");
DevicePairer.UnpairDevice(oldDevice);
DevicePairer.PairDevice(newDevice);
DevicePairer.PairDevice(newDevice, pin);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion Src/Command/Utils/DeviceType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static bool Equals(DeviceType type1, Bluetooth.DeviceType type2)
}

if ( type1 == DeviceType.Bluetooth && type2 == Bluetooth.DeviceType.Bluetooth
|| type1 == DeviceType.BluetoothLE && type2 == Bluetooth.DeviceType.BluetoothLe)
|| type1 == DeviceType.BluetoothLE && type2 == Bluetooth.DeviceType.BluetoothLE)
{
return true;
}
Expand Down

0 comments on commit c006e9f

Please sign in to comment.