public override void StartScanning() { BpLogger.Info("XInputGamepadManager start scanning"); try { var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two), new Controller(UserIndex.Three), new Controller(UserIndex.Four), }; foreach (var c in controllers) { if (!c.IsConnected) { continue; } BpLogger.Debug($"Found connected XInput Gamepad for Index {c.UserIndex}"); var device = new XInputGamepadDevice(LogManager, c); InvokeDeviceAdded(new DeviceAddedEventArgs(device)); InvokeScanningFinished(); } } catch (DllNotFoundException e) { // TODO Should we maybe try testing for this in construction instead of during scanning? BpLogger.Error($"Required DirectX DLL not found: {e.Message}\nThis probably means you need to install the DirectX Runtime from June 2010: https://www.microsoft.com/en-us/download/details.aspx?id=8109"); InvokeScanningFinished(); } }
protected override void RunScan() { try { // TODO this should scan in a loop on a timer until told to stop var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two), new Controller(UserIndex.Three), new Controller(UserIndex.Four), }; foreach (var c in controllers) { if (!c.IsConnected) { continue; } BpLogger.Debug($"Found connected XInput Gamepad for Index {c.UserIndex}"); var deviceImpl = new XInputGamepadDevice(LogManager, c); var device = new ButtplugDevice(LogManager, new XInputProtocol(LogManager, deviceImpl), deviceImpl); InvokeDeviceAdded(new DeviceAddedEventArgs(device)); // Do not invoke ScanningFinished here, the parent class will handle that for us. } } catch (DllNotFoundException e) { // TODO Should we maybe try testing for this in construction instead of during scanning? BpLogger.Error($"Required DirectX DLL not found: {e.Message}\nThis probably means you need to install the DirectX Runtime from June 2010: https://www.microsoft.com/en-us/download/details.aspx?id=8109"); throw e; } }
private async void CuemeUpdateHandler(object aEvent, ElapsedEventArgs aArgs) { if (_vibratorSpeeds.SequenceEqual(NullSpeed)) { _updateValueTimer.Enabled = false; _vibeIndex = 0; } var data = Convert.ToByte((int)(_vibratorSpeeds[_vibeIndex] * 15)); if (data != 0x00) { data |= (byte)((byte)(_vibeIndex + 1) << 4); } // We'll have to use an internal token here since this is timer triggered. try { // todo This throw doesn't actually go anywhere. This should bubble upward. await Interface.WriteValueAsync(new[] { data }, _stopUpdateCommandSource.Token).ConfigureAwait(false); } catch (ButtplugDeviceException ex) { BpLogger.Error($"Cannot send update to Cueme {_devInfo.Name}, device may stick on a single vibrator."); _updateValueTimer.Enabled = false; } if (!_updateValueTimer.Enabled || aArgs == null) { return; } // Move to the next active vibe var nextVibe = _vibeIndex; while (true) { nextVibe++; // Wrap back to 0 if (nextVibe == _devInfo.VibeCount) { nextVibe = 0; } if (Math.Abs(_vibratorSpeeds[nextVibe]) > 0.0) { _vibeIndex = nextVibe; break; } if (nextVibe == _vibeIndex) { // Stop the timer: there's only one vibe running _updateValueTimer.Enabled = false; break; } } }
public VorzeSA(IButtplugLogManager aLogManager, IBluetoothDeviceInterface aInterface, IBluetoothDeviceInfo aInfo) : base(aLogManager, "Vorze SA Unknown", aInterface, aInfo) { switch (aInterface.Name) { case "CycSA": _deviceType = DeviceType.CycloneOrUnknown; _commandType = CommandType.Rotate; Name = "Vorze A10 Cyclone SA"; break; case "UFOSA": _deviceType = DeviceType.UFO; _commandType = CommandType.Rotate; Name = "Vorze UFO SA"; break; case "Bach smart": _deviceType = DeviceType.Bach; _commandType = CommandType.Vibrate; Name = "Vorze Bach"; break; default: // If the device doesn't identify, warn and try sending it Cyclone packets. BpLogger.Warn($"Vorze product with unrecognized name ({Name}) found. This product may not work with B******g. Contact the developers for more info."); break; } switch (_commandType) { case CommandType.Rotate: AddMessageHandler <VorzeA10CycloneCmd>(HandleVorzeA10CycloneCmd); AddMessageHandler <RotateCmd>(HandleRotateCmd, new MessageAttributes() { FeatureCount = 1 }); break; case CommandType.Vibrate: AddMessageHandler <SingleMotorVibrateCmd>(HandleSingleMotorVibrateCmd); AddMessageHandler <VibrateCmd>(HandleVibrateCmd, new MessageAttributes() { FeatureCount = 1 }); break; default: BpLogger.Error("Unhandled command type."); break; } AddMessageHandler <StopDeviceCmd>(HandleStopDeviceCmd); }
public override void StartScanning() { _scanning = true; var hidDevices = new HidEnumerator(); foreach (var hid in hidDevices.Enumerate()) { try { hid.ReadProduct(out var product); hid.ReadManufacturer(out var vendor); var prod = Encoding.Unicode.GetString(product); var vend = Encoding.Unicode.GetString(vendor); prod = prod.Substring(0, prod.IndexOf('\0')); vend = vend.Substring(0, vend.IndexOf('\0')); BpLogger.Trace("Found HID device (" + hid.Attributes.VendorHexId + ":" + hid.Attributes.ProductHexId + "): " + vend + " - " + prod); var factories = _deviceFactories.Where(x => x.MayBeDevice(hid.Attributes.VendorId, hid.Attributes.ProductId)); var buttplugHidDeviceFactories = factories as HidDeviceFactory[] ?? factories.ToArray(); if (buttplugHidDeviceFactories.Length != 1) { if (buttplugHidDeviceFactories.Any()) { BpLogger.Warn($"Found multiple HID factories for {hid.Attributes.VendorHexId}:{hid.Attributes.ProductHexId}"); buttplugHidDeviceFactories.ToList().ForEach(x => BpLogger.Warn(x.GetType().Name)); } else { // BpLogger.Trace("No BLE factories found for device."); } continue; } var factory = buttplugHidDeviceFactories.First(); BpLogger.Debug($"Found HID factory: {factory.GetType().Name}"); var d = factory.CreateDevice(hid); InvokeDeviceAdded(new DeviceAddedEventArgs(d)); } catch (Exception e) { // TODO Figure out what exceptions can actually be thrown here. BpLogger.Error(e.Message); } } _scanning = false; InvokeScanningFinished(); }
private async void MysteryVibeUpdateHandler(object aEvent, ElapsedEventArgs aArgs) { if (_vibratorSpeeds.SequenceEqual(NullSpeed)) { _updateValueTimer.Enabled = false; } if (await Interface.WriteValue(ButtplugConsts.DefaultMsgId, (uint)MysteryVibeBluetoothInfo.Chrs.MotorControl, _vibratorSpeeds) is Error errorMsg) { BpLogger.Error($"Cannot send update to {Name}, device may stop moving."); _updateValueTimer.Enabled = false; } }
private async void OnOnyxTimer(object state) { lock (_onyxLock) { // Given the time since the last iteration and the difference in distance, work out have far we should move var distance = Math.Abs(_targetPosition - _currentPosition); var oldTime = new DateTime(_currentTime.Ticks); var nowTime = DateTime.Now; var delta = nowTime.Subtract(oldTime); if (delta.TotalMilliseconds < 50) { // Skip. Don't flood BLE return; } if (Convert.ToUInt32(distance * 4) == 0 && delta.TotalMilliseconds < 500) { // Skip. We do want to occasionally ping the Onyx, but only every half a second return; } _currentTime = nowTime; if (_currentTime.CompareTo(_targetTime) >= 0 || Convert.ToUInt32(distance * 4) == 0) { // We've overdue: jump to target _currentPosition = _targetPosition; _onyxTimer?.Change(500, 500); } else { // The hard part: find the percentage time gone, then add that percentage of the movement delta var delta2 = _targetTime.Subtract(oldTime); var move = (Convert.ToDouble(delta.TotalMilliseconds) / (Convert.ToDouble(delta2.TotalMilliseconds) + 1)) * distance; _currentPosition += move * (_targetPosition > _currentPosition ? 1 : -1); _currentPosition = Math.Max(0, Math.Min(1, _currentPosition)); } } var res = await HandleKiirooRawCmd(new KiirooCmd(0, Convert.ToUInt32(_currentPosition * 4), ButtplugConsts.SystemMsgId), default(CancellationToken)).ConfigureAwait(false); if (res is Error err) { BpLogger.Error(err.ErrorMessage); } }
public override void StartScanning() { BpLogger.Info("Starting Scanning Serial Ports for ErosTek Devices"); _isScanning = true; foreach (var entry in _portDeviceTypeMap) { var device = entry.Value.CreateDevice(LogManager, entry.Key); if (device == null) { BpLogger.Error($"Cannot open device on port {entry.Key}."); continue; } device.InitializeAsync().Wait(); } }
private async void MysteryVibeUpdateHandler(object aEvent, ElapsedEventArgs aArgs) { if (_vibratorSpeeds.SequenceEqual(NullSpeed)) { _stopUpdateCommandSource.Cancel(); _updateValueTimer.Enabled = false; } // We'll have to use an internal token here since this is timer triggered. if (await Interface.WriteValueAsync(ButtplugConsts.DefaultMsgId, (uint)MysteryVibeBluetoothInfo.Chrs.MotorControl, _vibratorSpeeds, false, _stopUpdateCommandSource.Token).ConfigureAwait(false) is Error) { BpLogger.Error($"Cannot send update to {Name}, device may stop moving."); _updateValueTimer.Enabled = false; } }
private async Task WriteValueAsync(ICharacteristic aChar, byte[] aValue, bool aWriteWithResponse, CancellationToken aToken) { if (!(_currentWriteTokenSource is null)) { _internalTokenSource.Cancel(); BpLogger.Error("Cancelling device transfer in progress for new transfer."); } try { _internalTokenSource = new CancellationTokenSource(); _currentWriteTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_internalTokenSource.Token, aToken); var writeTask = aChar.WriteAsync(aValue, _currentWriteTokenSource.Token); var status = await writeTask.ConfigureAwait(false); _currentWriteTokenSource = null; if (_bleDevice.State != DeviceState.Connected) { throw new ButtplugDeviceException(BpLogger, $"GattCommunication Error: {status}"); } } catch (InvalidOperationException e) { // This exception will be thrown if the bluetooth device disconnects in the middle of // a transfer. throw new ButtplugDeviceException(BpLogger, $"GattCommunication Error: {e.Message}"); } catch (TaskCanceledException e) { // This exception will be thrown if the bluetooth device disconnects in the middle of // a transfer (happened when MysteryVibe lost power). throw new ButtplugDeviceException(BpLogger, $"Device disconnected: {e.Message}"); } }
private async void MysteryVibeUpdateHandler(object aEvent, ElapsedEventArgs aArgs) { if (_vibratorSpeeds.SequenceEqual(NullSpeed)) { _stopUpdateCommandSource.Cancel(); _updateValueTimer.Enabled = false; } // We'll have to use an internal token here since this is timer triggered. try { await Interface.WriteValueAsync(_vibratorSpeeds, new ButtplugDeviceWriteOptions { Endpoint = Endpoints.TxVibrate }, _stopUpdateCommandSource.Token).ConfigureAwait(false); } catch (ButtplugException ex) { // todo We should do more here. BpLogger.Error($"Cannot send update to {Name}, device may stop moving."); _updateValueTimer.Enabled = false; } }
protected void OnScanTimer(object aObj, ElapsedEventArgs aArgs) { if (_scanLock.CurrentCount == 0) { return; } try { _scanLock.Wait(); RunScan(); } catch (Exception e) { BpLogger.Error($"Caught timed-repeat scanning exception (aborting further scans for {GetType().Name})."); BpLogger.Error($"Exception Message: ${e.Message}"); _scanTimer.Enabled = false; InvokeScanningFinished(); } finally { _scanLock.Release(); } }
protected override void RunScan() { BpLogger.Info("VibHubManager start scanning"); try { var devices = File.ReadAllLines(@"vibhub.txt"); if (!devices.Any()) { return; } if (!socket.Connected) { socket.ConnectAsync(); socket.EmitAsync("app", "B******g Server"); } foreach (var dev in devices) { if (dev.Length > 0) { socket.EmitAsync("hookup", data => HookupAck(data, dev), dev); } } } catch (FileNotFoundException e) { BpLogger.Info(e.Message); } catch (Exception e) { BpLogger.Error(e.Message); } InvokeScanningFinished(); }
private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher aObj, BluetoothLEAdvertisementReceivedEventArgs aEvent) { if (aEvent?.Advertisement == null) { BpLogger.Debug("Null BLE advertisement recieved: 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 (_currentlyConnecting.Contains(btAddr)) { // BpLogger.Trace($"Ignoring advertisement for already connecting device: {aEvent.Advertisement.LocalName} / {aEvent.BluetoothAddress}"); return; } BpLogger.Trace("BLE device found: " + advertName); var factories = from x in _deviceFactories where x.MayBeDevice(advertName, advertGUIDs) select x; // We should always have either 0 or 1 factories. var buttplugBluetoothDeviceFactories = factories as UWPBluetoothDeviceFactory[] ?? factories.ToArray(); if (buttplugBluetoothDeviceFactories.Length != 1) { if (buttplugBluetoothDeviceFactories.Any()) { BpLogger.Warn($"Found multiple BLE factories for {advertName} {btAddr}:"); buttplugBluetoothDeviceFactories.ToList().ForEach(x => BpLogger.Warn(x.GetType().Name)); } else { // BpLogger.Trace("No BLE factories found for device."); } return; } _currentlyConnecting.Add(btAddr); var factory = buttplugBluetoothDeviceFactories.First(); BpLogger.Debug($"Found BLE factory: {factory.GetType().Name}"); // If we actually have a factory for this device, go ahead and create the device var fromBluetoothAddressAsync = BluetoothLEDevice.FromBluetoothAddressAsync(btAddr); if (fromBluetoothAddressAsync != null) { 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. try { var d = await factory.CreateDeviceAsync(dev); InvokeDeviceAdded(new DeviceAddedEventArgs(d)); } catch (Exception ex) { BpLogger.Error( $"Cannot connect to device {advertName} {btAddr}: {ex.Message}"); _currentlyConnecting.Remove(btAddr); return; } } _currentlyConnecting.Remove(btAddr); }
protected override void RunScan() { foreach (var factory in DeviceConfigurationManager.Manager.GetAllFactoriesOfType <USBProtocolConfiguration>()) { BpLogger.Info("WinUSBManager start scanning"); var deviceKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\USB\VID_0B49&PID_064F"); if (deviceKey == null) { BpLogger.Debug("No TranceVibrator Devices found in registry."); InvokeScanningFinished(); return; } var deviceKeyNames = deviceKey.GetSubKeyNames(); if (deviceKeyNames.Length == 0) { BpLogger.Debug("No TranceVibrator Devices with drivers found in registry."); InvokeScanningFinished(); return; } var deviceSubKey = deviceKey.OpenSubKey(deviceKeyNames[0]); if (deviceSubKey == null) { BpLogger.Debug("No TranceVibrator Devices with drivers subkeys found in registry."); InvokeScanningFinished(); return; } var deviceParameters = deviceSubKey.OpenSubKey("Device Parameters"); if (deviceParameters == null) { BpLogger.Debug("No TranceVibrator Devices with drivers parameters found in registry."); InvokeScanningFinished(); return; } var deviceRegistryObject = deviceParameters.GetValue("DeviceInterfaceGUIDs", string.Empty); string deviceGuid = string.Empty; if (deviceRegistryObject == null) { BpLogger.Debug("No TranceVibrator Devices Driver GUIDs found in registry."); InvokeScanningFinished(); return; } try { var guidStrings = (string[])deviceRegistryObject; deviceGuid = guidStrings[0]; } catch (Exception) { try { // Some versions of Zadig, the registry key writes as a string, not a string array? deviceGuid = (string)deviceRegistryObject; } catch (Exception) { BpLogger.Error("Cannot cast device GUID from registry value, cannot connect to Trancevibe."); } } if (deviceGuid.Length == 0) { BpLogger.Error("Cannot find device GUID from registry value, cannot connect to Trancevibe."); return; } // Only valid for our Trancevibrator install var devices = USBDevice.GetDevices(deviceGuid); if (devices == null || devices.Length == 0) { BpLogger.Error("No USB Device found!"); InvokeScanningFinished(); return; } uint index = 0; foreach (var deviceinfo in devices) { var device = new USBDevice(deviceinfo); BpLogger.Debug("Found TranceVibrator Device"); var bpDevice = factory.CreateDevice(LogManager, new WinUSBDeviceImpl(LogManager, device)).Result; InvokeDeviceAdded(new DeviceAddedEventArgs(bpDevice)); } } InvokeScanningFinished(); }
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}"); } }
public override async Task <ButtplugMessage> InitializeAsync(CancellationToken aToken) { BpLogger.Trace($"Initializing {Name}"); // Subscribing to read updates await Interface.SubscribeToUpdatesAsync().ConfigureAwait(false); Interface.BluetoothNotifyReceived += NotifyReceived; // Retreiving device type info for identification. var writeMsg = await Interface.WriteValueAsync(ButtplugConsts.SystemMsgId, Encoding.ASCII.GetBytes("DeviceType;"), true, aToken).ConfigureAwait(false); if (writeMsg is Error) { BpLogger.Error($"Error requesting device info from Lovense {Name}"); return(writeMsg); } var deviceInfoString = string.Empty; try { var(msg, result) = await Interface.ReadValueAsync(ButtplugConsts.SystemMsgId, aToken).ConfigureAwait(false); if (msg is Ok && result.Length > 0) { deviceInfoString = Encoding.ASCII.GetString(result); } } catch (ButtplugDeviceException) { // The device info notification isn't available immediately. // TODO Turn this into a task semaphore with cancellation/timeout, let system handle check timing. int timeout = 1000; while (timeout > 0) { if (_lastNotifyReceived != string.Empty) { deviceInfoString = _lastNotifyReceived; break; } timeout -= 5; await Task.Delay(5).ConfigureAwait(false); } } BpLogger.Debug($"Received device query return for {Interface.Name}"); // Expected Format X:YY:ZZZZZZZZZZZZ X is device type leter YY is firmware version Z is // bluetooth address var deviceInfo = deviceInfoString.Split(':'); // If we don't get back the amount of tokens we expect, identify as unknown, log, bail. if (deviceInfo.Length != 3 || deviceInfo[0].Length != 1) { throw new ButtplugDeviceException(BpLogger, $"Unknown Lovense DeviceType of {deviceInfoString} found. Please report to B******g Developers by filing an issue at https://github.com/buttplugio/b******g/"); } var deviceTypeLetter = deviceInfo[0][0]; if (deviceTypeLetter == 'C') { deviceTypeLetter = 'A'; } int.TryParse(deviceInfo[1], out var deviceVersion); BpLogger.Trace($"Lovense DeviceType Return: {deviceTypeLetter}"); if (!Enum.IsDefined(typeof(LovenseDeviceType), (uint)deviceTypeLetter)) { // If we don't know what device this is, just assume it has a single vibrator, call // it unknown, log something. AddCommonMessages(); throw new ButtplugDeviceException(BpLogger, $"Unknown Lovense Device of Type {deviceTypeLetter} found. Please report to B******g Developers by filing an issue at https://github.com/buttplugio/b******g/"); } Name = $"Lovense {Enum.GetName(typeof(LovenseDeviceType), (uint)deviceTypeLetter)} v{deviceVersion}"; _deviceType = (LovenseDeviceType)deviceTypeLetter; if (_deviceType == LovenseDeviceType.Unknown) { BpLogger.Error("Lovense device type unknown, treating as single vibrator device. Please contact developers for more info."); } switch (_deviceType) { case LovenseDeviceType.Edge: // Edge has 2 vibrators _vibratorCount++; break; case LovenseDeviceType.Nora: // Nora has a rotator AddMessageHandler <RotateCmd>(HandleRotateCmd, new MessageAttributes { FeatureCount = 1 }); break; } // Common messages. AddCommonMessages(); return(new Ok(ButtplugConsts.SystemMsgId)); }
public override async Task InitializeAsync(CancellationToken aToken) { BpLogger.Trace($"Initializing {Name}"); // Subscribing to read updates await Interface.SubscribeToUpdatesAsync().ConfigureAwait(false); Interface.DataReceived += NotifyReceived; // Retrieving device type info for identification. await Interface.WriteValueAsync(Encoding.ASCII.GetBytes("DeviceType;"), new ButtplugDeviceWriteOptions { WriteWithResponse = true }, aToken).ConfigureAwait(false); await Task.WhenAny(_notificationWaiter.Task, Task.Delay(4000, aToken)); var deviceInfoString = _notificationWaiter.Task.Result; BpLogger.Debug($"Received device query return for {Name}"); // Expected Format X:YY:ZZZZZZZZZZZZ X is device type leter YY is firmware version Z is // bluetooth address var deviceInfo = deviceInfoString.Split(':'); // If we don't get back the amount of tokens we expect, identify as unknown, log, bail. if (deviceInfo.Length != 3 || deviceInfo[0].Length != 1) { throw new ButtplugDeviceException(BpLogger, $"Unknown Lovense DeviceType of {deviceInfoString} found. Please report to B******g Developers by filing an issue at https://github.com/buttplugio/b******g/"); } var deviceTypeLetter = deviceInfo[0][0]; if (deviceTypeLetter == 'C') { deviceTypeLetter = 'A'; } int.TryParse(deviceInfo[1], out var deviceVersion); BpLogger.Trace($"Lovense DeviceType Return: {deviceTypeLetter}"); if (!Enum.IsDefined(typeof(LovenseDeviceType), (uint)deviceTypeLetter)) { // If we don't know what device this is, just assume it has a single vibrator, call // it unknown, log something. AddCommonMessages(); throw new ButtplugDeviceException(BpLogger, $"Unknown Lovense Device of Type {deviceTypeLetter} found. Please report to B******g Developers by filing an issue at https://github.com/buttplugio/b******g/"); } Name = $"Lovense {Enum.GetName(typeof(LovenseDeviceType), (uint)deviceTypeLetter)} v{deviceVersion}"; _deviceType = (LovenseDeviceType)deviceTypeLetter; if (_deviceType == LovenseDeviceType.Unknown) { BpLogger.Error("Lovense device type unknown, treating as single vibrator device. Please contact developers for more info."); } switch (_deviceType) { case LovenseDeviceType.Edge: // Edge has 2 vibrators _vibratorCount++; break; case LovenseDeviceType.Nora: // Nora has a rotator AddMessageHandler <RotateCmd>(HandleRotateCmd, new MessageAttributes { FeatureCount = 1 }); break; } // Common messages. AddCommonMessages(); }
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}"); } }
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) { BpLogger.Debug($"Auto finding characteristics for {_bleDevice.Name}"); await AddDefaultCharacteristics(serviceInfo.Key).ConfigureAwait(false); } else { var serviceGuid = serviceInfo.Key; GattDeviceService service; try { service = await GetService(serviceGuid).ConfigureAwait(false); } catch (ButtplugDeviceException ex) { // 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. BpLogger.Error(ex.Message); continue; } var chrResult = await service.GetCharacteristicsAsync(); if (chrResult.Status != GattCommunicationStatus.Success) { throw new ButtplugDeviceException(BpLogger, $"Cannot connect to characteristics for {serviceGuid} of {_bleDevice.Name}: {chrResult.Status}"); } foreach (var chr in chrResult.Characteristics) { foreach (var indexChr in serviceInfo.Value) { if (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}"); } }
public override async Task <ButtplugMessage> Initialize() { BpLogger.Trace($"Initializing {Name}"); // Subscribing to read updates await Interface.SubscribeToUpdates(); Interface.BluetoothNotifyReceived += NotifyReceived; // Retreiving device type info for identification. var writeMsg = await Interface.WriteValue(ButtplugConsts.SystemMsgId, Encoding.ASCII.GetBytes($"DeviceType;"), true); if (writeMsg is Error) { BpLogger.Error($"Error requesting device info from Lovense {Name}"); return(writeMsg); } var(msg, result) = await Interface.ReadValue(ButtplugConsts.SystemMsgId); string deviceInfoString = string.Empty; if (msg is Ok) { deviceInfoString = Encoding.ASCII.GetString(result); } else { // The device info notification isn't available immediately. // TODO Turn this into a task semaphore with cancellation/timeout, let system handle check timing. int timeout = 500; while (timeout > 0) { if (_lastNotifyReceived != string.Empty) { deviceInfoString = _lastNotifyReceived; break; } timeout -= 5; await Task.Delay(5); } } if (deviceInfoString != string.Empty) { BpLogger.Debug($"Received device query return for {Interface.Name}"); // Expected Format X:YY:ZZZZZZZZZZZZ X is device type leter YY is firmware version Z // is bluetooth address var deviceInfo = deviceInfoString.Split(':'); // If we don't get back the amount of tokens we expect, identify as unknown, log, bail. if (deviceInfo.Length != 3 || deviceInfo[0].Length != 1) { return(BpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, Error.ErrorClass.ERROR_DEVICE, $"Unknown Lovense DeviceType of {deviceInfoString} found. Please report to B******g Developers by filing an issue at https://github.com/metafetish/b******g/")); } var deviceTypeLetter = deviceInfo[0][0]; if (deviceTypeLetter == 'C') { deviceTypeLetter = 'A'; } int.TryParse(deviceInfo[1], out var deviceVersion); BpLogger.Trace($"Lovense DeviceType Return: {deviceTypeLetter}"); if (!Enum.IsDefined(typeof(LovenseDeviceType), (uint)deviceTypeLetter)) { // If we don't know what device this is, just assume it has a single vibrator, // call it unknown, log something. return(BpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, Error.ErrorClass.ERROR_DEVICE, $"Unknown Lovense Device of Type {deviceTypeLetter} found. Please report to B******g Developers by filing an issue at https://github.com/metafetish/b******g/")); } Name = $"Lovense {Enum.GetName(typeof(LovenseDeviceType), (uint)deviceTypeLetter)} v{deviceVersion}"; _deviceType = (LovenseDeviceType)deviceTypeLetter; } else { // If we for some reason don't get a device info query back, use fallback method. // // TODO Remove this branch at some point? Not sure we'll need it now since device queries seem stable. BpLogger.Warn($"Error retreiving device info from Lovense {Name}, using fallback method"); // Some of the older devices seem to have issues with info lookups? Not sure why, so // for now use fallback method. switch (Interface.Name.Substring(0, 6)) { case "LVS-B0": _deviceType = LovenseDeviceType.Max; break; case "LVS-A0": case "LVS-C0": _deviceType = LovenseDeviceType.Nora; break; case "LVS-L0": _deviceType = LovenseDeviceType.Ambi; break; default: _deviceType = LovenseDeviceType.Unknown; break; } Name = $"Lovense {Enum.GetName(typeof(LovenseDeviceType), (uint)_deviceType)} v{Interface.Name.Substring(Interface.Name.Length - 2)}"; } if (_deviceType == LovenseDeviceType.Unknown) { BpLogger.Error("Lovense device type unknown, treating as single vibrator device. Please contact developers for more info."); } switch (_deviceType) { case LovenseDeviceType.Edge: // Edge has 2 vibrators _vibratorCount++; MsgFuncs.Remove(typeof(VibrateCmd)); MsgFuncs.Add(typeof(VibrateCmd), new ButtplugDeviceWrapper(HandleVibrateCmd, new MessageAttributes() { FeatureCount = _vibratorCount })); break; case LovenseDeviceType.Nora: // Nora has a rotator MsgFuncs.Add(typeof(RotateCmd), new ButtplugDeviceWrapper(HandleRotateCmd, new MessageAttributes() { FeatureCount = 1 })); break; } return(new Ok(ButtplugConsts.SystemMsgId)); }
public override async Task <ButtplugMessage> InitializeAsync(CancellationToken aToken) { BpLogger.Info("Attempting Synch"); _boxKey = 0; // We shouldn't need to lock here, as this is called before any other commands can be sent. try { // Try to read a byte from RAM using an unencrypted command. If this works, we are // not synched yet. await SendCommand(new byte[] { (byte)ET312Consts.SerialCommand.Read, // read byte command 0, // address high byte 0, // address low byte }).ConfigureAwait(false); var result = await ReadAsync(1, aToken).ConfigureAwait(false); if (result.Length == 1 && result[0] == ((byte)ET312Consts.SerialResponse.Read | 0x20)) { await InitUnsynced().ConfigureAwait(false); } else { await InitSynced().ConfigureAwait(false); } // Setup box for remote control await Execute((byte)ET312Consts.BoxCommand.FavouriteMode).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelAGateSelect, (byte)ET312Consts.Gate.Off).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelBGateSelect, (byte)ET312Consts.Gate.Off).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelAIntensitySelect, (byte)ET312Consts.Select.Static).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelBIntensitySelect, (byte)ET312Consts.Select.Static).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelAIntensity, 0).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelBIntensity, 0).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelARampValue, 255).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelAFrequencySelect, (byte)ET312Consts.Select.MA).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelBFrequencySelect, (byte)ET312Consts.Select.MA).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelAWidthSelect, (byte)ET312Consts.Select.Advanced).ConfigureAwait(false); await Poke((uint)ET312Consts.RAM.ChannelBWidthSelect, (byte)ET312Consts.Select.Advanced).ConfigureAwait(false); // WriteLCD has its own lock. _lock.Release(); // Let the user know we're in control now await WriteLCD("B******g", 8).ConfigureAwait(false); await WriteLCD("----------------", 64).ConfigureAwait(false); // We're now ready to receive events AddMessageHandler <FleshlightLaunchFW12Cmd>(HandleFleshlightLaunchCmd); AddMessageHandler <LinearCmd>(HandleLinearCmd, new MessageAttributes { FeatureCount = 1 }); AddMessageHandler <StopDeviceCmd>(HandleStopDeviceCmd); // Start update timer _updateTimer = new Timer() { Interval = _updateInterval, AutoReset = true, Enabled = true, }; _updateTimer.Elapsed += OnUpdate; } catch (Exception ex) { BpLogger.Error("Synch with ET312 Failed"); // Release the lock before disconnecting await DisconnectInternal(false).ConfigureAwait(false); if (ex is ErostekET312CommunicationException || ex is InvalidOperationException || ex is TimeoutException) { throw new ErostekET312HandshakeException("Failed to set up communications with device."); } throw; } return(new Ok(ButtplugConsts.SystemMsgId)); }