public static async Task <IButtplugDeviceImpl> Create(IButtplugLogManager aLogManager, BluetoothLEProtocolConfiguration aConfig, IDevice aDevice) { var device = new XamarinBluetoothDeviceInterface(aLogManager, aDevice); await device.InitializeDevice(aConfig).ConfigureAwait(false); return(device); }
protected async Task InitializeDevice(BluetoothLEProtocolConfiguration aConfig) { foreach (var serviceInfo in aConfig.Services) { // If we don't have any characteristic configuration, assume we're using // characteristic detection. if (serviceInfo.Value == null || serviceInfo.Value.Count == 0) { await AddDefaultCharacteristics(serviceInfo.Key).ConfigureAwait(false); } else { var serviceGuid = serviceInfo.Key; IService service; try { service = await GetService(serviceGuid).ConfigureAwait(false); } catch (ButtplugDeviceException) { // In this case, we may have a whole bunch of services that aren't valid for // a device and only one that is. We can ignore the exception here, and throw // later if we don't get anything from any service in the list. continue; } var chrResult = await service.GetCharacteristicsAsync(); if (service.Device.State != DeviceState.Connected) { throw new ButtplugDeviceException(BpLogger, $"Cannot connect to characteristics for {serviceGuid} of {_bleDevice.Name}: {service.Device.State}"); } foreach (var chr in chrResult) { foreach (var indexChr in serviceInfo.Value) { if (Guid.Parse(chr.Uuid) != indexChr.Value) { continue; } if (_indexedChars.ContainsKey(indexChr.Key)) { // We've somehow doubled up endpoint names. Freak out. throw new ButtplugDeviceException(BpLogger, $"Found repeated endpoint name {indexChr.Key} on {Name}"); } BpLogger.Debug($"Found characteristic {indexChr.Key} {chr.Uuid} ({_bleDevice.Name})"); _indexedChars.Add(indexChr.Key, chr); } } } } // If we've exited characteristic finding without any characteristics to use, something // is wrong with our configuration and we won't be able to talk to the device. Don't // continue connection. if (!_indexedChars.Any()) { throw new ButtplugDeviceException(BpLogger, $"No characteristics to connect to for device {Name}"); } }
private async void _adapter_DeviceAdvertised(object sender, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs e) { if (e?.Device == null) { BpLogger.Debug("Null BLE advertisement received: skipping"); return; } string advertName = e.Device.GetPropValue <string>("BluetoothDevice.Name"); var advertGUIDs = new List <Guid>(); advertGUIDs.Add(e.Device.Id); var btAddr = e.Device.GetPropValue <string>("BluetoothDevice.Address"); BpLogger.Trace($"Got BLE Advertisement for device: {advertName} / {btAddr}"); if (_seenAddresses.Contains(btAddr)) { BpLogger.Trace($"Ignoring advertisement for already connecting device: {btAddr}"); return; } _seenAddresses.Add(btAddr); BpLogger.Trace("BLE device found: " + advertName); // We always need a name to match against. if (String.IsNullOrEmpty(advertName)) { return; } // todo Add advertGUIDs back in. Not sure that ever really gets used though. var deviceCriteria = new BluetoothLEProtocolConfiguration(advertName); var deviceFactory = DeviceConfigurationManager.Manager.Find(deviceCriteria); // If we don't have a protocol to match the device, we can't do anything with it. if (deviceFactory == null || !(deviceFactory.Config is BluetoothLEProtocolConfiguration bleConfig)) { BpLogger.Debug($"No usable device factory available for {advertName}."); return; } // If a device is turned on after scanning has started, windows seems to lose the device // handle the first couple of times it tries to deal with the advertisement. Just log the // error and hope it reconnects on a later retry. IButtplugDeviceImpl bleDevice = null; IButtplugDevice btDevice = null; try { await _adapter.ConnectToDeviceAsync(e.Device); bleDevice = await XamarinBluetoothDeviceInterface.Create(LogManager, bleConfig, e.Device).ConfigureAwait(false); btDevice = await deviceFactory.CreateDevice(LogManager, bleDevice).ConfigureAwait(false); InvokeDeviceAdded(new DeviceAddedEventArgs(btDevice)); } catch (Exception ex) { if (btDevice != null) { btDevice.Disconnect(); } else { bleDevice?.Disconnect(); } BpLogger.Error( $"Cannot connect to device {advertName} {btAddr}: {ex.Message}"); } }
private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher aObj, BluetoothLEAdvertisementReceivedEventArgs aEvent) { if (aEvent?.Advertisement == null) { BpLogger.Debug("Null BLE advertisement received: skipping"); return; } var advertName = aEvent.Advertisement.LocalName ?? string.Empty; var advertGUIDs = new List <Guid>(); advertGUIDs.AddRange(aEvent.Advertisement.ServiceUuids ?? new Guid[] { }); var btAddr = aEvent.BluetoothAddress; // BpLogger.Trace($"Got BLE Advertisement for device: {aEvent.Advertisement.LocalName} / {aEvent.BluetoothAddress}"); if (_seenAddresses.Contains(btAddr)) { // BpLogger.Trace($"Ignoring advertisement for already connecting device: // {aEvent.Advertisement.LocalName} / {aEvent.BluetoothAddress}"); return; } BpLogger.Trace("BLE device found: " + advertName); // We always need a name to match against. if (advertName == string.Empty) { return; } // todo Add advertGUIDs back in. Not sure that ever really gets used though. var deviceCriteria = new BluetoothLEProtocolConfiguration(advertName); var deviceFactory = DeviceConfigurationManager.Manager.Find(deviceCriteria); // If we don't have a protocol to match the device, we can't do anything with it. if (deviceFactory == null || !(deviceFactory.Config is BluetoothLEProtocolConfiguration bleConfig)) { BpLogger.Debug($"No usable device factory available for {advertName}."); // If we've got an actual name this time around, and we don't have any factories // available that match the info we have, add to our seen list so we won't keep // rechecking. If a device does have a factory, but doesn't connect, we still want to // try again. _seenAddresses.Add(btAddr); return; } var fromBluetoothAddressAsync = BluetoothLEDevice.FromBluetoothAddressAsync(btAddr); // Can return null if the device no longer exists, for instance if it turned off between // advertising and us getting here. Since we didn't get a chance to try to connect, // remove it from seen devices, since the user may turn it back on during this scanning period. if (fromBluetoothAddressAsync == null) { return; } var dev = await fromBluetoothAddressAsync; // If a device is turned on after scanning has started, windows seems to lose the device // handle the first couple of times it tries to deal with the advertisement. Just log the // error and hope it reconnects on a later retry. IButtplugDeviceImpl bleDevice = null; IButtplugDevice btDevice = null; try { bleDevice = await UWPBluetoothDeviceInterface.Create(LogManager, bleConfig, dev).ConfigureAwait(false); btDevice = await deviceFactory.CreateDevice(LogManager, bleDevice).ConfigureAwait(false); InvokeDeviceAdded(new DeviceAddedEventArgs(btDevice)); } catch (Exception ex) { if (btDevice != null) { btDevice.Disconnect(); } else { bleDevice?.Disconnect(); } BpLogger.Error( $"Cannot connect to device {advertName} {btAddr}: {ex.Message}"); } }