Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the COM port name for a known Bluetooth device in UWP

I'm using a DeviceWatcher to get the DeviceInformation for a paired Bluetooth device in a UWP app. I set the DeviceWatcher up like this

var requestedProperties = new string[] { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
var deviceWatcher = DeviceInformation.CreateWatcher("(System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\")", requestedProperties, DeviceInformationKind.AssociationEndpoint); // ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974} includes all Bluetooth devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Start();

When the DeviceWatcher_Added event handler is called I check to see if the device is the one I am interested in by checking its name and that it offers the RfcommServiceId.SerialPort.Uuid service.

Once I have the DeviceInformation for the bluetooth device I am interested in how do I get the COM port for it? I can see it in the Device Manager, where it is listed as "Standard Serial over Bluetooth link (COM8)", but I cannot see how to get that "COM8" in UWP programmatically.

I've tried making the DeviceInformation into a SerialDevice, whereby I could then get SerialDevice.PortName (c.f. this answer) but my call to SerialDevice.FromIdAsync(deviceInfo.Id) fails with a System.Exception: The data is invalid.

(N.B. Some tantalizing answers, like this and this, use the Windows Management Intrumentation WMI functions but these are not available in UWP.)

like image 762
dumbledad Avatar asked Jun 01 '17 12:06

dumbledad


1 Answers

On another question Rita suggested looking at the Serial UART sample which helped me see a way to do this. I won't mark this as the answer for a while as it seems too indirect to be the canonical way.

Although I have the the DeviceInformation for the paired Bluetooth device in my UWP app I also need the list of SerialDevices so that I can match them up. Here's the resulting code.

public async Task<string> ComPort(DeviceInformation deviceInfo)
{
    var serialDevices = new Dictionary<string, SerialDevice>();
    var serialSelector = SerialDevice.GetDeviceSelector();
    var serialDeviceInformations = (await DeviceInformation.FindAllAsync(serialSelector)).ToList();
    var hostNames = NetworkInformation.GetHostNames().Select(hostName => hostName.DisplayName.ToUpper()).ToList(); // So we can ignore inbuilt ports
    foreach (var serialDeviceInformation in serialDeviceInformations)
    {
        if (hostNames.FirstOrDefault(hostName => hostName.StartsWith(serialDeviceInformation.Name.ToUpper())) == null)
        {
            try
            {
                var serialDevice = await SerialDevice.FromIdAsync(serialDeviceInformation.Id);
                if (serialDevice != null)
                {
                    serialDevices.Add(deviceInfo.Id, serialDevice);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
        }
    }
    // Example Bluetooth DeviceInfo.Id: "Bluetooth#Bluetooth9c:b6:d0:d6:d7:56-00:07:80:cb:56:6d"
    // from device with Association Endpoint Address: "00:07:80:cb:56:6d"
    var lengthOfTrailingAssociationEndpointAddresss = (2 * 6) + 5;
    var bluetoothDeviceAddress = deviceInfo.Id.Substring(deviceInfo.Id.Length - lengthOfTrailingAssociationEndpointAddresss, lengthOfTrailingAssociationEndpointAddresss).Replace(":", "").ToUpper();
    var matchingKey = serialDevices.Keys.FirstOrDefault(id => id.Contains(bluetoothDeviceAddress));
    if (matchingKey != null)
    {
        return serialDevices[matchingKey].PortName;
    }
    return "";
}
like image 126
dumbledad Avatar answered Oct 13 '22 13:10

dumbledad