示例#1
0
        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();
            }
        }
示例#2
0
        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;
            }
        }
示例#3
0
        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;
                }
            }
        }
示例#4
0
        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);
        }
示例#5
0
        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();
        }
示例#6
0
        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;
            }
        }
示例#7
0
        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);
            }
        }
示例#8
0
        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();
            }
        }
示例#9
0
        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}");
            }
        }
示例#11
0
        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;
            }
        }
示例#12
0
        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();
            }
        }
示例#13
0
        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);
        }
示例#15
0
        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();
        }
示例#16
0
        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}");
            }
        }
示例#17
0
        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));
        }
示例#18
0
        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}");
            }
        }
示例#21
0
        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));
        }
示例#22
0
        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));
        }