/// <summary> /// /// </summary> /// <param name="characteristic"></param> /// <param name="useIndicationIfAvailable"></param> /// <returns></returns> public static IObservable <CharacteristicResult> RegisterAndNotify(this IGattCharacteristic characteristic, bool useIndicationIfAvailable = false) => characteristic .EnableNotifications(useIndicationIfAvailable) .Where(x => x) .Select(x => characteristic.WhenNotificationReceived()) .Switch() .Finally(() => characteristic.DisableNotifications().Subscribe());
/// <summary> /// Enables notifications and hooks it for discovered characteristic. When subscription is disposed, it will also clean up. /// </summary> /// <param name="characteristic"></param> /// <param name="useIndicationIfAvailable"></param> /// <returns></returns> public static IObservable <CharacteristicGattResult> RegisterAndNotify(this IGattCharacteristic characteristic, bool useIndicationIfAvailable = false) => characteristic .EnableNotifications(useIndicationIfAvailable) .Select(x => x.Characteristic.WhenNotificationReceived()) .Switch() .Finally(() => characteristic .DisableNotifications() .Where(x => x.Characteristic.Service.Device.Status == ConnectionStatus.Connected) .Subscribe( _ => {}, ex => Log.Warn( BleLogCategory.Characteristic, "Could not cleanly disable notifications in RegisterAndNotify - " + ex ) ) );
/// <summary> /// Enables notifications and hooks it for discovered characteristic. When subscription is disposed, it will also clean up. /// </summary> /// <param name="characteristic"></param> /// <param name="useIndicationIfAvailable"></param> /// <param name="autoCleanup"></param> /// <returns></returns> public static IObservable <CharacteristicGattResult> RegisterAndNotify(this IGattCharacteristic characteristic, bool useIndicationIfAvailable = false, bool autoCleanup = true) { var ob = characteristic .EnableNotifications(useIndicationIfAvailable) .Select(x => x.Characteristic.WhenNotificationReceived()) .Switch(); if (autoCleanup) { ob = ob.Finally(() => characteristic .DisableNotifications() .Where(x => x.Characteristic.Service.Peripheral.Status == ConnectionState.Connected) .Subscribe( _ => { }, ex => Log.Write(ex) ) ); } return(ob); }
/// <summary> /// Switch device in Secure DFU state /// ! IMPORTANT, assumes that each device has different name ! /// </summary> /// <param name="device"></param> /// <returns>Device which is in buttonles state</returns> private async Task <IDevice> ButtonlessDFUWithoutBondsToSecureDFU(IDevice device) { Debug.WriteLineIf(LogLevelDebug, String.Format("Start of Buttonless switching")); //await RefreshGattAsync(device); IGattCharacteristic buttonlessCharacteristic = null; TaskCompletionSource <CharacteristicGattResult> notif = null; device.Connect(); device = await device.ConnectWait().Timeout(DeviceConnectionTimeout); buttonlessCharacteristic = await device.GetKnownCharacteristics(DfuService, DfuButtonlessCharacteristicWithoutBonds).Timeout(OperationTimeout); await buttonlessCharacteristic.EnableNotifications(true).Timeout(OperationTimeout); // Change device Name // Advertisment name should not be longer than 20 symbols var newFullName = device.Name + "DFU"; var newName = new string(newFullName.Take(20).ToArray()); byte[] name = Encoding.ASCII.GetBytes(newName); int newNameLen = name.Length; byte[] newNameCommand = new byte[newNameLen + 1 + 1]; newNameCommand[0] = CChangeAdvertisedName; newNameCommand[1] = (byte)(uint)newNameLen; name.CopyTo(newNameCommand, 2); var nameChangeNotif = GetTimedNotification(buttonlessCharacteristic); await buttonlessCharacteristic.Write(newNameCommand).Timeout(OperationTimeout); var nameChangeResult = await nameChangeNotif.Task; Debug.WriteLineIf(LogLevelDebug, String.Format("Device name change response {0}", nameChangeResult.Data != null ? BitConverter.ToString(nameChangeResult.Data) : "Empty")); // Jump from the main application to Secure DFU bootloader (Secure DFU mode) notif = GetTimedNotification(buttonlessCharacteristic); await buttonlessCharacteristic.Write(CEnterDFU).Timeout(OperationTimeout); var result = await notif.Task; Debug.WriteLineIf(LogLevelDebug, String.Format("Restart response {0}", result.Data != null ? BitConverter.ToString(result.Data) : "Empty")); await buttonlessCharacteristic.DisableNotifications(); /* * The response received from the DFU device contains: * +---------+--------+----------------------------------------------------+ * | byte no | value | description | * +---------+--------+----------------------------------------------------+ * | 0 | 0x20 | Response code | * | 1 | 0x01 | The Op Code of a request that this response is for | * | 2 | STATUS | Status code | * +---------+--------+----------------------------------------------------+ */ int status = result.Data[2]; if (status != ButtonlessSwitchSuccessCode) { throw new Exception("Init status not correct " + status); } bool alreadyRestarted = false; IDisposable dispose = null; dispose = device.WhenStatusChanged().Subscribe(res => { if (res == ConnectionStatus.Disconnected || res == ConnectionStatus.Disconnecting) { alreadyRestarted = true; } Debug.WriteLine("###################### STAT {0}", res); }); dispose?.Dispose(); if (!alreadyRestarted) { await device.WhenDisconnected().Take(1).Timeout(DeviceRestartTimeout); device.CancelConnection(); } IDevice newDevice = await ScanDFUDevice(device, newName); Debug.WriteLineIf(LogLevelDebug, String.Format("End of Buttonless switching")); return(newDevice); }
/// <summary> /// Close connections, try to reenter standart mode, unsubscribe notifications /// </summary> /// <returns></returns> private async Task Cleanup(IGattCharacteristic controlPoint, IDevice device) { await controlPoint.DisableNotifications(); device.CancelConnection(); }