private async Task <GattDescriptorsResult> GetDescriptorsAsyncImpl(BluetoothCacheMode cacheMode)
        {
            IReadOnlyList <GattDescriptor> descriptors = null;
            List <GattDescriptor>          found       = new List <GattDescriptor>();

#if WIN32
            foreach (Windows.Devices.Bluetooth.GenericAttributeProfile.GattDescriptor d in _characteristic.GetAllDescriptors())
            {
                found.Add(d);
            }
#else
            // Update for Creators Update
            var result = await _characteristic.GetDescriptorsAsync();

            if (result != null)
            {
                foreach (Windows.Devices.Bluetooth.GenericAttributeProfile.GattDescriptor d in result.Descriptors)
                {
                    found.Add(d);
                }
            }
#endif

            if (found.Count > 0)
            {
                descriptors = found.AsReadOnly();
            }

            return(new GattDescriptorsResult(descriptors));
        }
Beispiel #2
0
        protected async Task <object> PollNow(string deviceId, BluetoothCacheMode mode)
        {
            try
            {
                using (var device = await CreateDevice(deviceId))
                {
                    var characteristic = await CreateCharacteristic(device, mode);

                    GattReadResult result = await characteristic.ReadValueAsync(mode);

                    if (result.Status != GattCommunicationStatus.Success)
                    {
                        throw result.ToException();
                    }

                    // force descriptors
                    var r = await characteristic.GetDescriptorsAsync();

                    // presentation format - first if available
                    var format = characteristic.PresentationFormats.FirstOrDefault();

                    if (format == null)
                    {
                        return(result.ToValue(DefaultPresentationFormat));
                    }

                    return(result.ToValue(format));
                }
            }
            catch (ArgumentException)
            {
                return(null);
            }
        }
        public async Task <byte> ReadBatteryLevelAsync(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            GattStatus gattStatus = GattStatus.Unsupported;

            var batteryLevelCharacteristic = this.GetCharacteristic(GattCharacteristicUuids.BatteryLevel);

            if (batteryLevelCharacteristic == null)
            {
                gattStatus = GattStatus.UnknownCharacteristic;
            }
            else
            {
                GattReadResult readResult = await batteryLevelCharacteristic.ReadValueAsync(cacheMode);

                gattStatus = (GattStatus)readResult.Status;
                if (gattStatus == GattStatus.Success)
                {
                    using (DataReader reader = DataReader.FromBuffer(readResult.Value))
                    {
                        return(reader.ReadByte());
                    }
                }
            }

            OnError("ReadBatteryLevelAsync:" + gattStatus.ToString());
            return(0);
        }
Beispiel #4
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadQWIIC(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(5, "QWIIC", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "OEB U8|HEX|Sensor U16|HEX|Channel1 U16|HEX|Channel2 U16|HEX|Channel3 U16|HEX|Channel4 U16|HEX|Channel5";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            QWIIC_Sensor = parseResult.ValueList.GetValue("Sensor").AsDouble;

            QWIIC_Channel1 = parseResult.ValueList.GetValue("Channel1").AsDouble;

            QWIIC_Channel2 = parseResult.ValueList.GetValue("Channel2").AsDouble;

            QWIIC_Channel3 = parseResult.ValueList.GetValue("Channel3").AsDouble;

            QWIIC_Channel4 = parseResult.ValueList.GetValue("Channel4").AsDouble;

            QWIIC_Channel5 = parseResult.ValueList.GetValue("Channel5").AsDouble;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #5
0
        private async Task <RfcommDeviceServicesResult> GetRfcommServicesAsyncImpl(BluetoothCacheMode cacheMode)
        {
            BluetoothError             error    = BluetoothError.Success;
            List <RfcommDeviceService> services = new List <RfcommDeviceService>();

            return(new RfcommDeviceServicesResult(error, services.AsReadOnly()));
        }
        public async Task <byte[]> ReadCharacteristicBytes(Guid characteristicGuid, BluetoothCacheMode cacheMode)
        {
            var ch = GetCharacteristic(characteristicGuid);

            if (ch != null)
            {
                var properties = ch.CharacteristicProperties;

                if ((properties & GattCharacteristicProperties.Read) != 0)
                {
                    var result = await ch.ReadValueAsync(cacheMode);

                    var status = result.Status;
                    if (status != GattCommunicationStatus.Success)
                    {
                        throw new Exception("Read failed: " + status.ToString());
                    }
                    IBuffer    buffer = result.Value;
                    uint       size   = buffer.Length;
                    DataReader reader = DataReader.FromBuffer(buffer);
                    byte[]     data   = new byte[size];
                    reader.ReadBytes(data);
                    return(data);
                }
                else
                {
                    throw new Exception(string.Format("Characteristic '{0}' does not support GattCharacteristicProperties.Read"));
                }
            }
            else
            {
                throw new Exception(string.Format("Characteristic '{0}' not found", characteristicGuid.ToString()));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadRegulatory_List(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(13, "Regulatory_List", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "U8|HEX|BodyType U8|HEX|BodyStructure STRING|ASCII|Data";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            Regulatory_List_BodyType = parseResult.ValueList.GetValue("BodyType").AsDouble;

            Regulatory_List_BodyStructure = parseResult.ValueList.GetValue("BodyStructure").AsDouble;

            Regulatory_List_Data = parseResult.ValueList.GetValue("Data").AsString;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="ObservableGattDeviceService" /> class.
 /// </summary>
 /// <param name="service">The service this class wraps</param>
 public ObservableGattDeviceService(GattDeviceService service, BluetoothCacheMode cacheMode)
 {
     CacheMode = cacheMode;
     Service   = service;
     Name      = GattServiceUuidHelper.ConvertUuidToName(service.Uuid);
     UUID      = Service.Uuid.ToString();
     GetAllCharacteristics();
 }
 private async Task <GattReadResult> DoReadValueAsync(BluetoothCacheMode cacheMode)
 {
     try
     {
         return(new GattReadResult(GattCommunicationStatus.Success, _characteristic.Value.ToArray()));
     }
     catch
     {
         return(new GattReadResult(GattCommunicationStatus.Unreachable, null));
     }
 }
Beispiel #10
0
 private async Task <GattReadResult> DoReadValueAsync(BluetoothCacheMode cacheMode)
 {
     try
     {
         return(new GattReadResult(GattCommunicationStatus.Success, ((NSData)_descriptor.Value).ToArray()));
     }
     catch
     {
         return(new GattReadResult(GattCommunicationStatus.Unreachable, null));
     }
 }
        private async Task <bool> GetAllPrimaryServices(BluetoothCacheMode cacheMode)
        {
            var    succeeded = false;
            string debugMsg  = String.Format("GetAllPrimaryServices: ");

            Debug.WriteLine(debugMsg + "Entering");

            // Get all the services for this device
            var result = await BluetoothLEDevice.GetGattServicesAsync(cacheMode);

            if (result.Status == GattCommunicationStatus.Success)
            {
                System.Diagnostics.Debug.WriteLine(debugMsg + "GetGattServiceAsync SUCCESS");

                lock (Services)
                {
                    foreach (var serv in result.Services)
                    {
                        if (!GattServiceUuidHelper.IsReserved(serv.Uuid))
                        {
                            var temp = new ObservableGattDeviceService(serv);
                            // This isn't awaited so that the user can disconnect while the services are still being enumerated
                            temp.Initialize();
                            Services.Add(temp);
                        }
                        else
                        {
                            serv.Dispose();
                        }
                    }
                    ServiceCount = Services.Count();
                }

                succeeded = true;
            }
            else if (result.Status == GattCommunicationStatus.ProtocolError)
            {
                ErrorText = debugMsg + "GetGattServiceAsync Error: Protocol Error - " + result.ProtocolError.Value;
                System.Diagnostics.Debug.WriteLine(ErrorText);
                string msg           = "Connection protocol error: " + result.ProtocolError.Value.ToString();
                var    messageDialog = new MessageDialog(msg, "Connection failures");
                await messageDialog.ShowAsync();
            }
            else if (result.Status == GattCommunicationStatus.Unreachable)
            {
                ErrorText = debugMsg + "GetGattServiceAsync Error: Unreachable";
                System.Diagnostics.Debug.WriteLine(ErrorText);
                string msg           = "Device unreachable";
                var    messageDialog = new MessageDialog(msg, "Connection failures");
                await messageDialog.ShowAsync();
            }

            return(succeeded);
        }
        private async Task <RfcommDeviceServicesResult> GetRfcommServicesAsyncImpl(BluetoothCacheMode cacheMode)
        {
            BluetoothError             error    = BluetoothError.Success;
            List <RfcommDeviceService> services = new List <Rfcomm.RfcommDeviceService>();

            foreach (Guid g in GetRfcommServices(ref _info))
            {
                services.Add(new Rfcomm.RfcommDeviceService(this, Rfcomm.RfcommServiceId.FromUuid(g)));
            }

            return(new Rfcomm.RfcommDeviceServicesResult(error, services.AsReadOnly()));
        }
Beispiel #13
0
        private async Task <GattReadResult> DoReadValueAsync(BluetoothCacheMode cacheMode)
        {
            bool success = true;

            if (cacheMode == BluetoothCacheMode.Uncached)
            {
                success = _service.Device._bluetoothGatt.ReadCharacteristic(_characteristic);
                _readHandle.WaitOne();
            }

            return(new GattReadResult(success ? GattCommunicationStatus.Success : GattCommunicationStatus.Unreachable, _characteristic.GetValue()));
        }
Beispiel #14
0
        public static SDPCacheMode ToBluetoothSDPCacheMode(this BluetoothCacheMode mode)
        {
            switch (mode)
            {
            default:
            case BluetoothCacheMode.Cached:
                return(SDPCacheMode.Cached);

            case BluetoothCacheMode.Uncached:
                return(SDPCacheMode.Uncached);
            }
        }
Beispiel #15
0
 private async Task ReadBatteryLevel(BluetoothLEDevice device, BluetoothCacheMode cacheMode)
 {
     try
     {
         batteryLevel = await ReadBluetoothValueByte(device, Constants.BTLESvcGuidBattery, Constants.BTLEChrGuidBattery, cacheMode);
     }
     catch (Exception)
     {
         batteryLevel = -1;
     }
     OnNotifyPropertyChanged($"{nameof(BatteryLevel)}");
     OnNotifyPropertyChanged($"{nameof(BatteryIconGlyph)}");
     OnNotifyPropertyChanged($"{nameof(BatteryIconColor)}");
 }
Beispiel #16
0
        private async Task ReadAllStatus(BluetoothLEDevice device, BluetoothCacheMode cacheMode)
        {
            try
            {
                await ReadBatteryLevel(device, cacheMode);
                await ReadTemperature(device, cacheMode);
                await ReadMode(device, cacheMode);

                //isConnected = device.ConnectionStatus == BluetoothConnectionStatus.Connected;
                isConnected = true;
            }
            catch (Exception)
            {
                isConnected = false;
            }
        }
Beispiel #17
0
        private Task <GattDescriptorsResult> GetDescriptorsAsyncImpl(BluetoothCacheMode cacheMode)
        {
            List <GattDescriptor> descriptors = new List <GattDescriptor>();

            try
            {
                foreach (BluetoothGattDescriptor descriptor in _characteristic.Descriptors)
                {
                    descriptors.Add(new GattDescriptor(this, descriptor));
                }

                return(Task.FromResult(new GattDescriptorsResult(GattCommunicationStatus.Success, descriptors.AsReadOnly())));
            }
            catch
            {
                return(Task.FromResult(new GattDescriptorsResult(GattCommunicationStatus.Unreachable, null)));
            }
        }
Beispiel #18
0
        private async Task ReadMode(BluetoothLEDevice device, BluetoothCacheMode cacheMode)
        {
            try
            {
                this.mode = await ReadBluetoothValueByte(device, Constants.BTLESvcGuidLight, Constants.BTLEChrGuidMode, cacheMode);
            }
            catch (Exception)
            {
                this.mode = 255;
            }

            foreach (var m in modes)
            {
                m.Active = m.ID == this.mode;
            }

            OnNotifyPropertyChanged($"{nameof(Mode)}");
        }
        private Task <GattDescriptorsResult> GetDescriptorsAsyncImpl(BluetoothCacheMode cacheMode)
        {
            IReadOnlyList <GattDescriptor> descriptors = null;
            // Update for Creators Update
            var result = _characteristic.GetAllDescriptors();

            if (result != null)
            {
                List <GattDescriptor> found = new List <GattDescriptor>();
                foreach (Windows.Devices.Bluetooth.GenericAttributeProfile.GattDescriptor d in result)
                {
                    found.Add(d);
                }

                descriptors = found.AsReadOnly();
            }

            return(Task.FromResult(new GattDescriptorsResult(descriptors)));
        }
Beispiel #20
0
        private async Task <RfcommDeviceServicesResult> GetRfcommServicesAsyncImpl(BluetoothCacheMode cacheMode)
        {
            BluetoothError             error    = BluetoothError.Success;
            List <RfcommDeviceService> services = new List <RfcommDeviceService>();

            if (cacheMode == BluetoothCacheMode.Uncached)
            {
                error = _device.FetchUuidsWithSdp() ? BluetoothError.Success : BluetoothError.DeviceNotConnected;
            }

            ParcelUuid[] uuids = _device.GetUuids();
            if (uuids != null)
            {
                foreach (ParcelUuid g in uuids)
                {
                    services.Add(new RfcommDeviceService(this, RfcommServiceId.FromUuid(new Guid(g.Uuid.ToString()))));
                }
            }

            return(new RfcommDeviceServicesResult(error, services.AsReadOnly()));
        }
Beispiel #21
0
        private async Task <RfcommDeviceServicesResult> GetRfcommServicesAsyncImpl(BluetoothCacheMode cacheMode)
        {
            BluetoothError             error    = BluetoothError.Success;
            List <RfcommDeviceService> services = new List <RfcommDeviceService>();

#if WINDOWS_PHONE_APP || WINDOWS_PHONE
            foreach (RfcommDeviceService service in _device.RfcommServices)
            {
                services.Add(service);
            }
#else
            var result = await _device.GetRfcommServicesAsync((Windows.Devices.Bluetooth.BluetoothCacheMode)((int)cacheMode));

            error = (BluetoothError)((int)result.Error);
            foreach (Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService service in result.Services)
            {
                services.Add(service);
            }
#endif
            return(new RfcommDeviceServicesResult(error, services.AsReadOnly()));
        }
Beispiel #22
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadBatteryLevel(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(15, "BatteryLevel", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "I8|DEC|BatteryLevel|%";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            BatteryLevel = parseResult.ValueList.GetValue("BatteryLevel").AsDouble;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadAmbientLight(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(7, "AmbientLight", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "OEB U16|HEX|AmbientLight|Lux";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            AmbientLight_AmbientLight = parseResult.ValueList.GetValue("AmbientLight").AsDouble;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #24
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadCentral_Address_Resolution(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(3, "Central_Address_Resolution", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "U8|DEC|AddressResolutionSupported";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            Central_Address_Resolution = parseResult.ValueList.GetValue("AddressResolutionSupported").AsDouble;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #25
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadConnection_Parameter(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(2, "Connection_Parameter", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "BYTES|HEX|ConnectionParameter";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            Connection_Parameter = parseResult.ValueList.GetValue("ConnectionParameter").AsString;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #26
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadAppearance(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(1, "Appearance", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "U16|Speciality^Appearance|Appearance";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            Appearance = parseResult.ValueList.GetValue("Appearance").AsDouble;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #27
0
        /// <summary>
        /// Reads data
        /// </summary>
        /// <param name="cacheMode">Caching mode. Often for data we want uncached data.</param>
        /// <returns>BCValueList of results; each result is named based on the name in the characteristic string. E.G. U8|Hex|Red will be named Red</returns>
        public async Task <BCBasic.BCValueList> ReadDevice_Name(BluetoothCacheMode cacheMode = BluetoothCacheMode.Uncached)
        {
            if (!await EnsureCharacteristicAsync())
            {
                return(null);
            }
            IBuffer result = await ReadAsync(0, "Device_Name", cacheMode);

            if (result == null)
            {
                return(null);
            }

            var datameaning = "STRING|ASCII|Device_Name";
            var parseResult = BluetoothDeviceController.BleEditor.ValueParser.Parse(result, datameaning);

            Device_Name = parseResult.ValueList.GetValue("Device_Name").AsString;

            // Hint: get the data that's been read with e.g.
            // var value = parseResult.ValueList.GetValue("LightRaw").AsDouble;
            return(parseResult.ValueList);
        }
Beispiel #28
0
        private async Task ReadTemperature(BluetoothLEDevice device, BluetoothCacheMode cacheMode)
        {
            try
            {
                var buffer = await ReadBluetoothValue(device, Constants.BTLESvcGuidLight, Constants.BTLEChrGuidTemperature, cacheMode);

                if (buffer[2] != 0)
                {
                    temperature = buffer[3] / 10.0;
                }
                else
                {
                    temperature = double.NaN;
                }
            }
            catch (Exception)
            {
                temperature = double.NaN;
            }
            OnNotifyPropertyChanged($"{nameof(Temperature)}");
            OnNotifyPropertyChanged($"{nameof(HasTemperature)}");
            OnNotifyPropertyChanged($"{nameof(TemperatureText)}");
        }
        private Task <GattDescriptorsResult> GetDescriptorsAsyncImpl(BluetoothCacheMode cacheMode)
        {
            List <GattDescriptor> descriptors = new List <GattDescriptor>();

            try
            {
                if (cacheMode == BluetoothCacheMode.Uncached)
                {
                    _service.Device.Peripheral.DiscoverDescriptors(_characteristic);
                }

                foreach (CBDescriptor d in _characteristic.Descriptors)
                {
                    descriptors.Add(d);
                }

                return(Task.FromResult(new GattDescriptorsResult(GattCommunicationStatus.Success, descriptors.AsReadOnly())));
            }
            catch
            {
                return(Task.FromResult(new GattDescriptorsResult(GattCommunicationStatus.Unreachable, null)));
            }
        }
Beispiel #30
0
        protected async Task <GattCharacteristic> CreateCharacteristic(BluetoothLEDevice deveice, BluetoothCacheMode mode)
        {
            var serviceResult = await deveice.GetGattServicesForUuidAsync(Service);

            if (serviceResult.Status != GattCommunicationStatus.Success)
            {
                //TODO process proper result message
                return(null);
            }

            foreach (var service in serviceResult.Services)
            {
                var result = await service.GetCharacteristicsForUuidAsync(Characteristic);

                if (result.Status == GattCommunicationStatus.Success)
                {
                    return(result.Characteristics.FirstOrDefault());
                }
            }

            return(null);
        }