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

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

François Beaufort
François Beaufort

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

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

قبل البدء

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

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

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

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

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

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

انتهت الفترة التجريبية بنجاح في يناير 2017.

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

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

‫HTTPS فقط

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

يجب أن يتخذ المستخدم إجراءً

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

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

التعمّق في الرمز البرمجي

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

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

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

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

طلب من مستخدم جهاز البلوتوث.

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

فلتر الخدمات

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

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

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

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

فلتر الأسماء

يمكنك أيضًا طلب أجهزة Bluetooth استنادًا إلى اسم الجهاز الذي يتم الإعلان عنه باستخدام مفتاح الفلاتر 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); });

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

يمكن أيضًا طلب أجهزة Bluetooth استنادًا إلى البيانات الخاصة بالشركة المصنّعة التي يتم الإعلان عنها باستخدام مفتاح الفلاتر 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);
}

الكتابة إلى سمة بلوتوث

الكتابة إلى سمة GATT في Bluetooth سهلة مثل قراءتها. في هذه المرة، سنستخدم نقطة التحكّم في معدّل نبضات القلب لإعادة ضبط قيمة حقل "الطاقة المستهلكة" على 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. يُرجى العِلم أنّه لن يتم إيقاف الاتصال بجهاز البلوتوث إذا كان تطبيق آخر يتواصل معه. يمكنك الاطّلاع على نموذج قطع الاتصال بالجهاز ونموذج إعادة الاتصال التلقائي للتعمّق أكثر في الموضوع.

قراءة أو كتابة أوصاف البلوتوث

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

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

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

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); });

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

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

مبتدئ

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

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

يمكنك أيضًا الاطّلاع على عروض Web Bluetooth التوضيحية المنسّقة وبرامج Web Bluetooth التعليمية الرسمية.

المكتبات

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

الأدوات

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

نصائح

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

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

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

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

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

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

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

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

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

أرسِل تغريدة إلى ‎@ChromiumDev باستخدام الهاشتاغ #WebBluetooth وأخبرنا عن مكان استخدامك لهذه الميزة وكيفية استخدامها.

الموارد

الإقرارات

نشكر Kayce Basques على مراجعتها.