التواصل مع الأجهزة التي تتضمّن بلوتوث عبر JavaScript

تسمح Web Bluetooth API للمواقع الإلكترونية بالاتصال بالأجهزة التي تتضمّن بلوتوث.

François Beaufort
François Beaufort

ماذا لو أخبرتك أن مواقع الويب يمكنها التواصل مع الأجهزة القريبة التي تتضمن بلوتوث بطريقة آمنة تحافظ على الخصوصية؟ بهذه الطريقة، يمكن أن تتفاعل أجهزة مراقبة معدّل نبضات القلب ومصابيح الإضاءة التي تغنّي وحتى السلاحف مباشرةً مع المواقع الإلكترونية.

حتى الآن، أصبحت القدرة على التفاعل مع الأجهزة التي تتضمّن بلوتوث متاحة فقط للتطبيقات الخاصة بالنظام الأساسي. تهدف Web Bluetooth API إلى تغيير هذا وجلبه إلى متصفحات الويب أيضًا.

قبل أن نبدأ

يفترض هذا المستند أن لديك بعض المعرفة الأساسية حول طريقة عمل البلوتوث المنخفض الطاقة (BLE) والملف التعريفي العام للسمة.

على الرغم من أنّ مواصفات Web Bluetooth API لم يتم الانتهاء منها بعد، فإنّ مؤلفي المواصفات يبحثون بجدية عن مطوّرين متحمّسين لتجربة واجهة برمجة التطبيقات هذه وتقديم ملاحظات حول المواصفات والملاحظات حول عملية التنفيذ.

تتوفّر مجموعة فرعية من Web Bluetooth API في ChromeOS وChrome لنظام التشغيل Android 6.0 وMac (Chrome 56) وWindows 10 (Chrome 70). هذا يعني أنّه من المفترض أن يكون بإمكانك طلب والاتصال بالأجهزة القريبة التي تتضمّن طاقة بلوتوث منخفضة، وقراءة/كتابة خصائص البلوتوث، وتلقّي إشعارات GATT، ومعرفة عندما ينقطع اتصال جهاز يتضمّن بلوتوث، وحتى القراءة والكتابة إلى أدوات وصف البلوتوث. راجِع جدول توافُق المتصفح في MDN للحصول على مزيد من المعلومات.

بالنسبة إلى نظام التشغيل Linux والإصدارات السابقة من Windows، يمكنك تفعيل العلامة #experimental-web-platform-features في about://flags.

متاحة لمرحلة التجربة والتقييم

للحصول على أكبر قدر ممكن من الملاحظات من المطوّرين الذين يستخدمون Web Bluetooth API في هذا المجال، سبق أن أضاف Chrome هذه الميزة في الإصدار 53 من Chrome كإصدار تجريبي من المصدر لأنظمة التشغيل ChromeOS وAndroid وMac.

انتهت الفترة التجريبية بنجاح في كانون الثاني (يناير) 2017.

متطلبات الأمان

لفهم مقايضة الأمان، أقترح مشاركة نموذج أمان البلوتوث على الويب الذي أعدّته "جيفري ياسكين"، وهو مهندس برمجيات في فريق Chrome، يعمل على مواصفات Web Bluetooth API.

HTTPS فقط

بما أنّ واجهة برمجة التطبيقات التجريبية هذه ميزة جديدة وفعّالة تمت إضافتها إلى الويب، لم يتم توفيرها سوى للسياقات الآمنة. وهذا يعني أنه ستحتاج إلى الإنشاء مع وضع بروتوكول أمان طبقة النقل (TLS) في الاعتبار.

يجب تفعيل إيماءة المستخدم.

كميزة أمان، يجب تفعيل إيماءة المستخدم، مثلاً اللمس أو النقر بالماوس، على الأجهزة التي تتضمّن بلوتوث navigator.bluetooth.requestDevice. نحن نتحدّث عن الاستماع إلى فعاليات pointerup وclick وtouchend.

button.addEventListener('pointerup', function(event) {
  // Call navigator.bluetooth.requestDevice
});

المشاركة في الرمز

تعتمد Web Bluetooth API بشكل كبير على وعود JavaScript. إذا لم تكن معتادًا على هذه الممارسات، يمكنك الاطّلاع على هذا البرنامج التعليمي الرائع للتعهدات. () => {} هي دوال الأسهم لعام 2015 في ECMAScript.

طلب أجهزة تتضمّن بلوتوث

يتيح هذا الإصدار من مواصفات Web Bluetooth API للمواقع الإلكترونية، التي تعمل في الدور المركزي، الاتصال بخوادم GATT عن بُعد عبر اتصال BLE. وهو يدعم التواصل بين الأجهزة التي تستخدم البلوتوث 4.0 أو الإصدارات الأحدث.

عندما يطلب أحد المواقع الإلكترونية الوصول إلى الأجهزة المجاورة باستخدام navigator.bluetooth.requestDevice، سيطلب المتصفّح من المستخدم أداة اختيار الأجهزة حيث يمكنه اختيار جهاز واحد أو إلغاء الطلب.

إشعار مستخدم جهاز يتضمّن بلوتوث

تستخدم الدالة navigator.bluetooth.requestDevice() كائنًا إلزاميًا يحدد عوامل التصفية. وتُستخدم هذه الفلاتر لعرض الأجهزة التي تتطابق فقط مع بعض خدمات Bluetooth GATT المُعلَن عنها و/أو اسم الجهاز.

فلتر الخدمات

على سبيل المثال، لطلب أجهزة بلوتوث التي تعلن عن خدمة بطارية GATT:

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });

إذا لم تكن خدمة Bluetooth GATT مدرَجة في قائمة خدمات Bluetooth GATT الموحّدة، يمكنك تقديم إما المعرّف الفريد العالمي (UUID) للبلوتوث بالكامل أو نموذج قصير 16 أو 32 بت.

navigator.bluetooth.requestDevice({
  filters: [{
    services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
  }]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

فلتر الأسماء

يمكنك أيضًا طلب أجهزة تتضمّن بلوتوث استنادًا إلى اسم الجهاز الذي يتم الإعلان عنه باستخدام مفتاح الفلاتر name أو حتى بادئة من هذا الاسم باستخدام مفتاح الفلاتر namePrefix. يُرجى العلم أنّه في هذه الحالة، يجب أيضًا تحديد المفتاح optionalServices لتتمكّن من الوصول إلى أي خدمات غير مضمّنة في فلتر الخدمة. وإذا لم تقم بذلك، فسوف تحصل على خطأ لاحقًا عند محاولة الوصول إليها.

navigator.bluetooth.requestDevice({
  filters: [{
    name: 'Francois robot'
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

فلتر بيانات الشركة المصنّعة

ومن الممكن أيضًا طلب أجهزة تتضمّن بلوتوث استنادًا إلى البيانات الخاصة بالشركة المصنّعة التي يتم الإعلان عنها باستخدام مفتاح الفلاتر manufacturerData. وهذا المفتاح هو مصفوفة من العناصر التي لها مفتاح معرّف شركة Bluetooth إلزامي اسمه companyIdentifier. يمكنك أيضًا توفير بادئة بيانات تعمل على تصفية بيانات الشركة المصنعة من أجهزة بلوتوث التي تبدأ بها. يُرجى العلم أنّه عليك أيضًا تحديد مفتاح optionalServices لتتمكّن من الوصول إلى أي خدمات غير مضمّنة في فلتر الخدمة. وإذا لم تقم بذلك، ستتلقى رسالة خطأ لاحقًا عند محاولة الوصول إليها.

// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
  filters: [{
    manufacturerData: [{
      companyIdentifier: 0x00e0,
      dataPrefix: new Uint8Array([0x01, 0x02])
    }]
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

يمكن أيضًا استخدام القناع مع بادئة البيانات لمطابقة بعض الأنماط في بيانات الشركة المصنّعة. يمكنك الاطّلاع على الشرح لفلاتر بيانات البلوتوث لمعرفة المزيد من المعلومات.

فلاتر الاستبعاد

يتيح لك الخيار exclusionFilters في navigator.bluetooth.requestDevice() استبعاد بعض الأجهزة من أداة اختيار المتصفّح. يمكن استخدامه لاستبعاد الأجهزة التي تتطابق مع فلتر أوسع ولكن غير متوافقة.

// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
  filters: [{
    namePrefix: "Created by"
  }],
  exclusionFilters: [{
    name: "Created by Francois"
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

عدم استخدام الفلاتر

أخيرًا، بدلاً من filters، يمكنك استخدام المفتاح acceptAllDevices لعرض جميع الأجهزة القريبة التي تتضمّن بلوتوث. عليك أيضًا تحديد مفتاح optionalServices لتتمكّن من الوصول إلى بعض الخدمات. وإذا لم تقم بذلك، فستتلقى رسالة خطأ لاحقًا عند محاولة الوصول إليها.

navigator.bluetooth.requestDevice({
  acceptAllDevices: true,
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

الاتصال بجهاز بلوتوث

إذًا، ماذا ستفعل الآن بعد أن أصبح لديك BluetoothDevice؟ دعونا نتصل بخادم GATT عن بعد عبر البلوتوث والذي يحمل تعريفات الخدمة والخصائص.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
  // Human-readable name of the device.
  console.log(device.name);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

قراءة إحدى خصائص البلوتوث

هنا نتصل بخادم GATT للجهاز البعيد الذي يتضمّن بلوتوث. نريد الآن الحصول على خدمة GATT الأساسية وقراءة إحدى الخصائص التي تنتمي إلى هذه الخدمة. لنجرب، على سبيل المثال، قراءة مستوى الشحن الحالي لبطارية الجهاز.

في المثال السابق، battery_level هي سمة مستوى البطارية الموحّدة.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
  // Getting Battery Service…
  return server.getPrimaryService('battery_service');
})
.then(service => {
  // Getting Battery Level Characteristic…
  return service.getCharacteristic('battery_level');
})
.then(characteristic => {
  // Reading Battery Level…
  return characteristic.readValue();
})
.then(value => {
  console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });

في حال استخدام إحدى سمات GATT المخصّصة للبلوتوث، يمكنك تقديم إما معرّف UUID الكامل للبلوتوث أو نموذج قصير 16 أو 32 بت إلى service.getCharacteristic.

يُرجى العِلم أنّه يمكنك أيضًا إضافة أداة معالجة حدث characteristicvaluechanged إلى إحدى السمات الخاصة للتعامل مع قراءة قيمتها. يمكنك الاطّلاع على نموذج تغيير قيمة السمة المخصّصة للقراءة أيضًا لمعرفة كيفية التعامل مع إشعارات GATT القادمة اختياريًا.

…
.then(characteristic => {
  // Set up event listener for when characteristic value changes.
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleBatteryLevelChanged);
  // Reading Battery Level…
  return characteristic.readValue();
})
.catch(error => { console.error(error); });

function handleBatteryLevelChanged(event) {
  const batteryLevel = event.target.value.getUint8(0);
  console.log('Battery percentage is ' + batteryLevel);
}

الكتابة إلى إحدى خصائص البلوتوث

تعد الكتابة إلى سمة Bluetooth GATT سهلة مثل قراءتها. هذه المرة، لنستخدم نقطة التحكم في معدل ضربات القلب لإعادة ضبط قيمة حقل الطاقة المستنفدة على 0 على جهاز مراقبة معدل ضربات القلب.

أعدك أنه ما مِن سحر هنا. ويمكنك الاطّلاع على كل هذه التفاصيل في صفحة خصائص نقطة التحكّم في معدّل نبضات القلب.

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
  // Writing 1 is the signal to reset energy expended.
  const resetEnergyExpended = Uint8Array.of(1);
  return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
  console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });

تلقّي إشعارات GATT

لنرَ الآن كيفية تلقّي إشعار عند تغيُّر خاصية قياس معدّل نبضات القلب على الجهاز:

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleCharacteristicValueChanged);
  console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });

function handleCharacteristicValueChanged(event) {
  const value = event.target.value;
  console.log('Received ' + value);
  // TODO: Parse Heart Rate Measurement value.
  // See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}

يوضّح لك نموذج الإشعارات كيفية إيقاف الإشعارات من خلال stopNotifications() وإزالة أداة معالجة أحداث characteristicvaluechanged التي تمت إضافتها بشكل صحيح.

إلغاء الربط بجهاز يتضمّن بلوتوث

لتوفير تجربة مستخدم أفضل، قد ترغب في الاستماع إلى أحداث إلغاء الربط ودعوة المستخدم إلى إعادة الاتصال:

navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
  // Set up event listener for when device gets disconnected.
  device.addEventListener('gattserverdisconnected', onDisconnected);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

function onDisconnected(event) {
  const device = event.target;
  console.log(`Device ${device.name} is disconnected.`);
}

يمكنك أيضًا الاتصال بـ device.gatt.disconnect() لإلغاء ربط تطبيق الويب بالجهاز الذي يتضمّن بلوتوث. سيؤدي هذا الإجراء إلى ظهور مستمعي حدث gattserverdisconnected الحاليين. تجدر الإشارة إلى أنه لن يتوقف اتصال جهاز البلوتوث إذا كان هناك تطبيق آخر يتصل بالفعل بالجهاز الذي يتضمّن بلوتوث. يمكنك الاطّلاع على نموذج إلغاء ربط الجهاز ونموذج إعادة الربط التلقائي لمزيد من التفاصيل.

قراءة أدوات الوصف عبر البلوتوث والكتابة فيها

أدوات وصف GATT لالبلوتوث هي سمات تصف قيمة مميّزة. ويمكنك قراءتها والكتابة بها بطريقة مشابهة لخصائص Bluetooth GATT.

لنرَ على سبيل المثال كيفية قراءة وصف المستخدم لفاصل زمني لقياس ميزان الحرارة الصحية للجهاز.

في المثال أدناه، يتم استخدام خدمة ميزان الحرارة Health، measurement_interval وسمة الفاصل الزمني للقياس، وgatt.characteristic_user_descriptionأداة وصف وصف المستخدم المميز.health_thermometer

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
  const decoder = new TextDecoder('utf-8');
  console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });

الآن وبعد أن قرأنا وصف المستخدم للفاصل الزمني للقياس في ميزان الحرارة الصحية للجهاز، لنتعرّف على كيفية تحديثه وكتابة قيمة مخصّصة.

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
  const encoder = new TextEncoder('utf-8');
  const userDescription = encoder.encode('Defines the time between measurements.');
  return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });

النماذج التجريبية والعروض التوضيحية والدروس التطبيقية حول الترميز

تم اختبار جميع نماذج بلوتوث الويب أدناه بنجاح. للحصول على هذه العيّنات على أكمل وجه، ننصحك بتثبيت [تطبيق BLE Peripheral Simulator Android] الذي يحاكي جهاز BLE الملحق بخدمة بطارية أو خدمة معدل ضربات القلب أو خدمة ميزان حرارة للصحة.

مبتدئ

  • معلومات الجهاز - لاسترداد معلومات الجهاز الأساسية من جهاز تقنية BLE.
  • مستوى البطارية: يمكنك استرداد معلومات البطارية من جهاز BLE الخاص بإعلانات البطارية.
  • إعادة ضبط الطاقة - إعادة ضبط الطاقة المستنفدة من جهاز BLE يُعلن عن معدّل نبضات القلب.
  • الخصائص المميزة - تعرض جميع خصائص سمة معينة من جهاز تقنية البلوتوث المنخفض الطاقة (BLE).
  • الإشعارات: يمكنك تفعيل الإشعارات المميزة وإيقافها من جهاز تقنية البلوتوث المنخفض الطاقة.
  • إلغاء ربط الجهاز: يمكنك قطع الاتصال وتلقّي إشعار بانقطاع الاتصال بجهاز BLE بعد الاتصال به.
  • الحصول على الخصائص - احصل على جميع خصائص الخدمة المُعلَن عنها من جهاز تقنية BLE.
  • الحصول على أدوات الوصف: احصل على الكلمات الوصفية لجميع خصائص الخدمة المُعلَن عنها من جهاز تقنية BLE.
  • فلتر بيانات الشركة المصنّعة: يمكنك استرداد معلومات الجهاز الأساسية من جهاز BLE الذي يطابق بيانات الشركة المصنّعة.
  • فلاتر الاستبعاد: يمكنك استرداد معلومات الجهاز الأساسية من جهاز يستخدم تقنية BLE من خلال عرض فلاتر الاستبعاد الأساسية.

الجمع بين عمليات متعددة

يمكنك أيضًا الاطّلاع على عروضنا التوضيحية التي تم تنظيمها حول البلوتوث على الويب والدروس التطبيقية الرسمية حول ترميز البلوتوث على الويب.

المكتبات

  • web-bluetooth-utils عبارة عن وحدة npm تضيف بعض وظائف الراحة إلى واجهة برمجة التطبيقات.
  • يتوفر مكوّن Web Bluetooth API في الوحدة المركزية noble، وهي الوحدة المركزية Node.js BLE الأكثر شيوعًا. ويتيح لك هذا إنشاء حزمة ويب/إضافة برامج إلى متصفّحات الويب بدون الحاجة إلى خادم WebSocket أو مكونات إضافية أخرى.
  • angular-web-Bluetooth هو وحدة لخدمة Angular تزيل كل النصوص النموذجية اللازمة لإعداد Web Bluetooth API.

الأدوات

  • بدء استخدام Web Bluetooth هو تطبيق ويب بسيط سيُنشئ كل رمز JavaScript النموذجي لبدء التفاعل مع جهاز يتضمّن بلوتوث. أدخِل اسم الجهاز، أو الخدمة، أو إحدى السمات، وحدِّد خصائصها، ومن ثم
  • إذا كنت مطوّر برامج بلوتوث حاليًا، سينشئ المكوِّن الإضافي Web Bluetooth Developer Studio أيضًا رمز JavaScript لتقنية البلوتوث على الويب لجهازك الذي يتضمّن بلوتوث.

نصائح

تتوفّر صفحة عناصر البلوتوث الداخلية في Chrome على about://bluetooth-internals حتى تتمكّن من فحص كل ما يتعلق بأجهزة البلوتوث القريبة: الحالة والخدمات والخصائص والميزات الوصفية.

لقطة شاشة للصفحة الداخلية لتصحيح أخطاء البلوتوث في Chrome
صفحة داخلية في Chrome لتصحيح أخطاء الأجهزة التي تتضمّن بلوتوث

أنصحك أيضًا بمراجعة الصفحة الرسمية كيفية الإبلاغ عن أخطاء البلوتوث في الويب، لأنّ تصحيح أخطاء البلوتوث قد يكون صعبًا في بعض الأحيان.

الخطوات التالية

تحقَّق أولاً من حالة تنفيذ المتصفّح والنظام الأساسي لمعرفة أجزاء Web Bluetooth API التي يتمّ تنفيذها حاليًا.

على الرغم من أنها لا تزال غير مكتملة، إليك نظرة سريعة على ما يمكن توقعه في المستقبل القريب:

  • سيتم البحث عن إعلانات BLE القريبة مع navigator.bluetooth.requestLEScan().
  • سيتتبّع حدث "serviceadded" الجديد خدمات GATT التي تتضمّن بلوتوث والمكتشفة حديثًا، بينما يتتبّع حدث serviceremoved الخدمات التي تمت إزالتها. وسيتم تنشيط حدث servicechanged جديد عند إضافة أي سمة و/أو واصف أو إزالتها من خدمة Bluetooth GATT.

إظهار الدعم لواجهة برمجة التطبيقات

هل تخطّط لاستخدام واجهة برمجة التطبيقات Web Bluetooth API؟ يساعد الدعم العام فريق Chrome في تحديد أولويات الميزات ويوضح لمورّدي المتصفِّح الآخرين مدى أهمية دعمهم.

يمكنك إرسال تغريدة إلى @ChromiumDev باستخدام الهاشتاغ #WebBluetooth وإعلامنا بمكان استخدامك لها وطريقة استخدامك لها.

المراجِع

شكر وتقدير

شكرًا لـ كايس باسك لمراجعة هذه المقالة. صورة رئيسية من SparkFun Electronics من بولدر، الولايات المتحدة الأمريكية