Skip to content

Commit 9b8473a

Browse files
committed
* Improve code structure
* Add "list-adapters" command line option * Use a BluetoothDevice.FromBluetoothAddressAsync method for discovering a device by mac address. It is simpler than using previously implemented approach.
1 parent d086e5c commit 9b8473a

23 files changed

+251
-217
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ BluetoothDevicePairing.exe unpair-by-mac --mac 12:34:56:78:9A:BC --type Bluetoot
3737
```
3838
BluetoothDevicePairing.exe unpair-by-name --name "MX Ergo" --type BluetoothLE
3939
```
40+
* List all Bluetooth adapters available you your machine
41+
```
42+
BluetoothDevicePairing.exe list-adapters
43+
```
4044

4145
# How it works
4246
The program uses [Windows.Devices.Enumeration API](https://docs.microsoft.com/en-us/uwp/api/Windows.Devices.Enumeration?redirectedfrom=MSDN&view=winrt-22000) to work with Bluetooth.

src/Bluetooth/Adapters/Adapter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public Adapter(Windows.Devices.Enumeration.DeviceInformation bluetoothAdapterInf
2020
adapterDevice = Windows.Devices.Bluetooth.BluetoothAdapter.FromIdAsync(bluetoothAdapterInfo.Id).GetAwaiter().GetResult();
2121
radio = adapterDevice.GetRadioAsync().GetAwaiter().GetResult();
2222
MacAddress = new AdapterMacAddress(adapterDevice);
23-
IsDefault = MacAddress.Equals(defaultAdapterMacAddress);
23+
IsDefault = MacAddress.RawAddess == defaultAdapterMacAddress.RawAddess;
2424
}
2525

2626
public override string ToString()
Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,13 @@
1-
using System;
2-
using System.Linq;
3-
using Windows.Devices.Bluetooth;
4-
51
namespace BluetoothDevicePairing.Bluetooth.Adapter
62
{
7-
internal sealed class AdapterMacAddress : IEquatable<AdapterMacAddress>
3+
internal sealed class AdapterMacAddress : MacAddress
84
{
9-
public string Address { get; }
10-
11-
public AdapterMacAddress(BluetoothAdapter adapter)
12-
{
13-
Address = string.Join(":", BitConverter.GetBytes(adapter.BluetoothAddress)
14-
.Reverse()
15-
.Skip(2)
16-
.Select(b => b.ToString("x2")));
17-
}
18-
19-
public override string ToString()
20-
{
21-
return Address;
22-
}
23-
24-
public bool Equals(AdapterMacAddress other)
25-
{
26-
if (other is null)
27-
{
28-
return false;
29-
}
30-
31-
if (ReferenceEquals(this, other))
32-
{
33-
return true;
34-
}
35-
36-
return Address == other.Address;
37-
}
38-
39-
public override bool Equals(object obj)
5+
public AdapterMacAddress(Windows.Devices.Bluetooth.BluetoothAdapter adapter) : base(adapter.BluetoothAddress)
406
{
41-
return ReferenceEquals(this, obj) || obj is AdapterMacAddress other && Equals(other);
427
}
438

44-
public override int GetHashCode()
9+
public AdapterMacAddress(string mac) : base(mac)
4510
{
46-
return Address != null ? Address.GetHashCode() : 0;
4711
}
4812
}
4913
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace BluetoothDevicePairing.Bluetooth.Devices
4+
{
5+
internal sealed class BluetoothDevice : Device
6+
{
7+
public readonly Windows.Devices.Bluetooth.BluetoothDevice device;
8+
protected override bool IsConnected => device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected;
9+
10+
private BluetoothDevice(Windows.Devices.Bluetooth.BluetoothDevice device) : base(device.DeviceInformation)
11+
{
12+
this.device = device;
13+
}
14+
15+
public static BluetoothDevice FromDeviceInfo(Windows.Devices.Enumeration.DeviceInformation info)
16+
{
17+
return new BluetoothDevice(Windows.Devices.Bluetooth.BluetoothDevice.FromIdAsync(info.Id).GetAwaiter().GetResult());
18+
}
19+
20+
public static BluetoothDevice FromMac(DeviceMacAddress mac)
21+
{
22+
return new BluetoothDevice(Windows.Devices.Bluetooth.BluetoothDevice.FromBluetoothAddressAsync(mac.RawAddess).GetAwaiter().GetResult());
23+
}
24+
}
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
3+
namespace BluetoothDevicePairing.Bluetooth.Devices
4+
{
5+
internal class BluetoothLeDevice : Device
6+
{
7+
private readonly Windows.Devices.Bluetooth.BluetoothLEDevice device;
8+
protected override bool IsConnected => device.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected;
9+
10+
private BluetoothLeDevice(Windows.Devices.Bluetooth.BluetoothLEDevice device) : base(device.DeviceInformation)
11+
{
12+
this.device = device;
13+
}
14+
15+
public static BluetoothLeDevice FromDeviceInfo(Windows.Devices.Enumeration.DeviceInformation info)
16+
{
17+
return new BluetoothLeDevice(Windows.Devices.Bluetooth.BluetoothLEDevice.FromIdAsync(info.Id).GetAwaiter().GetResult());
18+
}
19+
20+
public static BluetoothLeDevice FromMac(DeviceMacAddress mac)
21+
{
22+
var device = Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(mac.RawAddess).GetAwaiter().GetResult();
23+
if(device == null)
24+
{
25+
throw new Exception($"Can't create a BluetoothLE device from the provided mac address '{mac}'. Device with this mac address doesn't exist");
26+
}
27+
return new BluetoothLeDevice(device);
28+
}
29+
}
30+
}

src/Bluetooth/Devices/Device.cs

Lines changed: 19 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,42 @@
1-
using System;
2-
using System.Text.RegularExpressions;
3-
41
namespace BluetoothDevicePairing.Bluetooth.Devices
52
{
3+
internal enum ConnectionStatus
4+
{
5+
NotPaired,
6+
Paired,
7+
Connected
8+
}
9+
610
internal enum DeviceType
711
{
812
Bluetooth,
913
BluetoothLE
1014
}
1115

12-
internal sealed class Device
16+
internal abstract class Device
1317
{
1418
private readonly Windows.Devices.Enumeration.DeviceInformation info;
15-
private readonly Windows.Devices.Bluetooth.BluetoothDevice bluetoothDevice;
16-
private readonly Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLeDevice;
19+
protected abstract bool IsConnected { get; }
1720

18-
public string Id => info.Id;
21+
public ConnectionStatus ConnectionStatus =>
22+
IsConnected
23+
? ConnectionStatus.Connected
24+
: PairingInfo.IsPaired
25+
? ConnectionStatus.Paired
26+
: ConnectionStatus.NotPaired;
1927
public Windows.Devices.Enumeration.DeviceInformationPairing PairingInfo => info.Pairing;
20-
public bool IsPaired => info.Pairing.IsPaired;
21-
public DeviceMacAddress Mac { get; }
22-
public DeviceType Type { get; }
28+
public DeviceInfoId Id { get; }
2329
public string Name => info.Name;
24-
public bool IsConnected
25-
{
26-
get
27-
{
28-
if (bluetoothDevice != null)
29-
{
30-
return bluetoothDevice.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected;
31-
}
32-
else
33-
{
34-
return bluetoothLeDevice.ConnectionStatus == Windows.Devices.Bluetooth.BluetoothConnectionStatus.Connected;
35-
}
36-
}
37-
}
3830

39-
public Device(Windows.Devices.Enumeration.DeviceInformation info)
31+
protected Device(Windows.Devices.Enumeration.DeviceInformation info)
4032
{
4133
this.info = info;
42-
Mac = new DeviceMacAddress(info);
43-
Type = GetDeviceType(info);
44-
if (Type == DeviceType.Bluetooth)
45-
{
46-
bluetoothDevice = Windows.Devices.Bluetooth.BluetoothDevice.FromIdAsync(info.Id).GetAwaiter().GetResult();
47-
}
48-
else
49-
{
50-
bluetoothLeDevice = Windows.Devices.Bluetooth.BluetoothLEDevice.FromIdAsync(info.Id).GetAwaiter().GetResult();
51-
}
34+
Id = new DeviceInfoId(info);
5235
}
5336

5437
public override string ToString()
5538
{
56-
return $"name:'{Name}' mac:'{Mac}' type:'{Type}' Connected:'{IsConnected}' Paired:'{IsPaired}'";
57-
}
58-
59-
private static DeviceType GetDeviceType(Windows.Devices.Enumeration.DeviceInformation device)
60-
{
61-
var match = Regex.Match(device.Id, @"(^\w*)(#)");
62-
if (!match.Success)
63-
{
64-
throw new Exception($"Failed to extract the device type from the string '{device.Id}'");
65-
}
66-
67-
var type = match.Groups[1].Value;
68-
switch (type)
69-
{
70-
case "Bluetooth":
71-
return DeviceType.Bluetooth;
72-
case "BluetoothLE":
73-
return DeviceType.BluetoothLE;
74-
default:
75-
throw new Exception($"Wrong device type '{type}' extracted from '{device.Id}'");
76-
}
39+
return $"name:'{Name}' mac:'{Id.DeviceMac}' type:'{Id.DeviceType}' ConnectionStatus:'{ConnectionStatus}'";
7740
}
7841
}
7942
}
Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,54 @@
1+
using BluetoothDevicePairing.Bluetooth.Devices.Utils;
12
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Threading;
56

67
namespace BluetoothDevicePairing.Bluetooth.Devices
78
{
8-
internal static class DeviceDiscoverer
9+
internal class DiscoveryTime
910
{
10-
public static List<Device> DiscoverBluetoothDevices(int timeoutInSec)
11+
public int Seconds { get; }
12+
13+
public DiscoveryTime(int timeInSeconds)
1114
{
12-
return Discover(AsqFilter.BluetoothDevicesFilter(), timeoutInSec);
15+
if (timeInSeconds < 1 || timeInSeconds > 30)
16+
{
17+
throw new Exception($"discovery time should be in range [1; 30] but was {timeInSeconds}");
18+
}
19+
20+
Seconds = timeInSeconds;
1321
}
22+
}
1423

15-
public static List<Device> DiscoverPairedBluetoothDevices(int timeoutInSec)
24+
internal static class DeviceDiscoverer
25+
{
26+
public static List<Device> DiscoverBluetoothDevices(DiscoveryTime time)
1627
{
17-
return Discover(AsqFilter.PairedBluetoothDevicesFilter(), timeoutInSec);
28+
return Discover(AsqFilter.BluetoothDevicesFilter(), time);
1829
}
1930

20-
private static List<Device> Discover(AsqFilter filter, int discoveryTimeInSec)
31+
public static List<Device> DiscoverPairedBluetoothDevices(DiscoveryTime time)
2132
{
22-
Console.WriteLine($"Start discovering devices for {discoveryTimeInSec} seconds");
33+
return Discover(AsqFilter.PairedBluetoothDevicesFilter(), time);
34+
}
2335

24-
if (discoveryTimeInSec < 1 || discoveryTimeInSec > 30)
25-
{
26-
throw new Exception($"discovery time should be in range [1; 30] but was {discoveryTimeInSec}");
27-
}
36+
private static List<Device> Discover(AsqFilter filter, DiscoveryTime time)
37+
{
38+
Console.WriteLine($"Start discovering devices for {time.Seconds} seconds");
2839

2940
var watcher = new DeviceWatcher(filter);
3041
watcher.Start();
31-
Thread.Sleep(discoveryTimeInSec * 1000);
42+
Thread.Sleep(time.Seconds * 1000);
3243
var devices = watcher.Stop();
33-
return devices.Select(d => new Device(d)).ToList();
44+
return devices.Select(info => CreateDevice(info)).ToList();
45+
}
46+
47+
private static Device CreateDevice(Windows.Devices.Enumeration.DeviceInformation info)
48+
{
49+
return new DeviceInfoId(info).DeviceType == DeviceType.Bluetooth
50+
? BluetoothDevice.FromDeviceInfo(info)
51+
: BluetoothLeDevice.FromDeviceInfo(info);
3452
}
3553
}
3654
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using BluetoothDevicePairing.Bluetooth.Adapter;
2+
using System;
3+
using System.Text.RegularExpressions;
4+
5+
namespace BluetoothDevicePairing.Bluetooth.Devices
6+
{
7+
internal sealed class DeviceInfoId
8+
{
9+
public DeviceType DeviceType { get; }
10+
public AdapterMacAddress AdapterMac { get; }
11+
public DeviceMacAddress DeviceMac { get; }
12+
13+
public DeviceInfoId(Windows.Devices.Enumeration.DeviceInformation info)
14+
{
15+
var match = Regex.Match(info.Id, @"(^\w+)#(?<Type>Bluetooth|BluetoothLE)(?<AdapterMac>(..:){5}(..))-(?<DeviceMac>(..:){5}(..))$");
16+
if (!match.Success)
17+
{
18+
throw new Exception($"Failed to parse DeviceInformation.Id '{info.Id}'");
19+
}
20+
21+
DeviceType = match.Groups["Type"].Value == "Bluetooth" ? DeviceType.Bluetooth : DeviceType.BluetoothLE;
22+
AdapterMac = new AdapterMacAddress(match.Groups["AdapterMac"].Value);
23+
DeviceMac = new DeviceMacAddress(match.Groups["DeviceMac"].Value);
24+
}
25+
}
26+
}
Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,9 @@
1-
using System;
2-
using System.Text.RegularExpressions;
3-
using Windows.Devices.Enumeration;
4-
51
namespace BluetoothDevicePairing.Bluetooth.Devices
62
{
7-
internal sealed class DeviceMacAddress : IEquatable<DeviceMacAddress>
3+
internal sealed class DeviceMacAddress : MacAddress
84
{
9-
public DeviceMacAddress(DeviceInformation device)
5+
public DeviceMacAddress(string mac) : base(mac)
106
{
11-
var match = Regex.Match(device.Id, @"(..:){5}(..)$");
12-
if (!match.Success)
13-
{
14-
throw new Exception($"Failed to extract mac address from the string '{device.Id}'");
15-
}
16-
Address = match.Value.ToUpper();
17-
}
18-
19-
public DeviceMacAddress(string mac)
20-
{
21-
var match = Regex.Match(mac, @"^(..:){5}(..)$");
22-
if (!match.Success)
23-
{
24-
throw new Exception($"MacAddress address '{mac}' is not a valid mac address");
25-
}
26-
Address = mac;
27-
}
28-
29-
public string Address { get; }
30-
31-
public override string ToString()
32-
{
33-
return Address;
34-
}
35-
36-
public bool Equals(DeviceMacAddress other)
37-
{
38-
if (other is null)
39-
{
40-
return false;
41-
}
42-
43-
if (ReferenceEquals(this, other))
44-
{
45-
return true;
46-
}
47-
48-
return Address == other.Address;
49-
}
50-
51-
public override bool Equals(object obj)
52-
{
53-
return ReferenceEquals(this, obj) || obj is DeviceMacAddress other && Equals(other);
54-
}
55-
56-
public override int GetHashCode()
57-
{
58-
return Address != null ? Address.GetHashCode() : 0;
597
}
608
}
619
}

0 commit comments

Comments
 (0)