ה-API של Bluetooth באינטרנט מאפשר לאתרים לתקשר עם מכשירי Bluetooth.
מה יקרה אם אספר לכם שאתרים יכולים לתקשר עם מכשירי Bluetooth בקרבת מקום באופן מאובטח ושומר על הפרטיות? כך, מכשירי מעקב אחר קצב הלב, נורות שרות ואפילו צבים יכולים לקיים אינטראקציה ישירה עם אתר.
עד עכשיו, היכולת לקיים אינטראקציה עם מכשירי Bluetooth הייתה אפשרית רק באפליקציות ספציפיות לפלטפורמה. ה-Web Bluetooth API נועד לשנות את המצב הזה ולהביא אותו גם לדפדפני אינטרנט.
לפני שמתחילים
ההנחה במסמך הזה היא שיש לכם ידע בסיסי באופן שבו פועלים Bluetooth Low Energy (BLE) ופרופיל המאפיינים הגנרי.
המפרט של Web Bluetooth API עדיין לא הושלם, אבל מחברי המפרט מחפשים מפתחים נלהבים שינסו את ה-API הזה ויספקו משוב על המפרט ומשוב על ההטמעה.
קבוצת משנה של Web Bluetooth API זמינה ב-ChromeOS, ב-Chrome ל-Android 6.0, ב-Mac (Chrome 56) וב-Windows 10 (Chrome 70). כלומר, תוכלו לבקש מכשירי Bluetooth עם צריכת אנרגיה נמוכה בקרבת מקום ולהתחבר אליהם, לקרוא או לכתוב מאפייני Bluetooth, לקבל התראות GATT, לדעת מתי מכשיר Bluetooth מתנתק ואפילו לקרוא ולכתוב בתיאור של Bluetooth. מידע נוסף זמין בטבלה תאימות לדפדפנים של MDN.
ב-Linux ובגרסאות קודמות של Windows, מפעילים את הדגל #experimental-web-platform-features
בקובץ about://flags
.
זמינה לגרסאות מקור לניסיון
כדי לקבל כמה שיותר משוב ממפתחים שמשתמשים ב-Web Bluetooth API בשטח, הוספנו את התכונה הזו בעבר ל-Chrome 53 כגרסת מקור לניסיון ל-ChromeOS, ל-Android ול-Mac.
תקופת הניסיון הסתיימה בהצלחה בינואר 2017.
דרישות אבטחה
כדי להבין את הפשרות בנושא אבטחה, מומלץ לקרוא את המאמר Web Bluetooth Security Model (מודל האבטחה של Web Bluetooth) של Jeffrey Yasskin, מהנדס תוכנה בצוות Chrome שעובד על מפרט Web Bluetooth API.
רק HTTPS
מאחר ש-API הניסיוני הזה הוא תכונה חדשה וחזקה שנוספה לאינטרנט, הוא זמין רק להקשרים מאובטחים. המשמעות היא שצריך לפתח תוך התחשבות ב-TLS.
נדרשת תנועה של המשתמש
כחלק מתכונות האבטחה, כדי לזהות מכשירי Bluetooth באמצעות navigator.bluetooth.requestDevice
צריך לבצע מחווה של המשתמש, כמו מגע או לחיצה על העכבר. הכוונה היא להאזנה לאירועים מסוג pointerup
, click
ו-touchend
.
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
איך נכנסים לקוד
ה-API של Bluetooth באינטרנט מסתמך במידה רבה על Promises של JavaScript. אם אתם לא מכירים אותם, כדאי לעיין במדריך המצוין הזה ל-Promises. דבר נוסף, הערך () => {}
הוא פונקציית חץ של ECMAScript 2015.
בקשה למכשירי Bluetooth
הגרסה הזו של מפרט ה-Web Bluetooth API מאפשרת לאתרים שפועלים בתפקיד המרכזי להתחבר לשרתי GATT מרוחקים דרך חיבור BLE. הוא תומך בתקשורת בין מכשירים שתומכים ב-Bluetooth 4.0 ואילך.
כשאתר מבקש גישה למכשירים בקרבת מקום באמצעות navigator.bluetooth.requestDevice
, הדפדפן מציג למשתמש חלונית לבחירת מכשיר שבה הוא יכול לבחור מכשיר אחד או לבטל את הבקשה.
הפונקציה navigator.bluetooth.requestDevice()
מקבלת אובייקט חובה שמגדיר מסננים. המסננים האלה משמשים להחזרת מכשירים שתואמים לשירותי Bluetooth GATT מסוימים שפורסמו ו/או לשם המכשיר.
מסנן שירותים
לדוגמה, כדי לבקש מכשירי Bluetooth שיפרסמו את שירות הסוללה של Bluetooth GATT:
navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });
עם זאת, אם שירות ה-GATT של Bluetooth לא מופיע ברשימת שירותי ה-GATT של Bluetooth שהוגדרו כסטנדרט, אפשר לספק את ה-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
. אפשר גם לספק קידומת נתונים שסננת נתוני יצרן ממכשירי Bluetooth שמתחילים בה. חשוב לזכור שצריך להגדיר גם את המפתח 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); });
אפשר גם להשתמש במסכה עם קידומת נתונים כדי להתאים לדפוסים מסוימים בנתוני היצרן. מידע נוסף זמין במאמר הסבר על מסנני נתוני Bluetooth.
מסנני החרגה
אפשרות 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
כדי להציג את כל מכשירי ה-Bluetooth בסביבה. תצטרכו גם להגדיר את המפתח optionalServices
כדי לגשת לשירותים מסוימים. אם לא תעשו זאת, תופיע שגיאה מאוחר יותר כשתנסו לגשת אליהם.
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });
התחברות למכשיר Bluetooth
אז מה עושים עכשיו שיש לכם BluetoothDevice
? נתחבר לשרת ה-GATT המרוחק של Bluetooth שמכיל את הגדרות השירות והמאפיינים.
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); });
קריאת מאפיין Bluetooth
כאן אנחנו מתחברים לשרת ה-GATT של מכשיר ה-Bluetooth המרוחק. עכשיו אנחנו רוצים לקבל שירות 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 מותאם אישית של Bluetooth, אפשר לספק את מזהה ה-UUID המלא של Bluetooth או פורמט קצר של 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
כתיבת למאפיין 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
שנוסף.
ניתוק ממכשיר Bluetooth
כדי לספק חוויית משתמש טובה יותר, מומלץ להאזין לאירועי ניתוק ולהזמין את המשתמש להתחבר מחדש:
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()
כדי לנתק את אפליקציית האינטרנט ממכשיר ה-Bluetooth. הפעולה הזו תפעיל את רכיבי ההאזנה לאירועים הקיימים של gattserverdisconnected
. לתשומת ליבכם: הפעולה הזו לא תפסיק את התקשורת עם מכשיר ה-Bluetooth אם אפליקציה אחרת כבר מתקשרת עם מכשיר ה-Bluetooth. למידע נוסף, אפשר לעיין בדוגמה לניתוק מכשיר ובדוגמה לחיבור מחדש אוטומטי.
קריאה וכתיבה של מתארי Bluetooth
מתארי GATT של Bluetooth הם מאפיינים שמתארים ערך של מאפיין. אפשר לקרוא אותן ולכתוב בהן באופן דומה למאפייני GATT של Bluetooth.
לדוגמה, נראה איך קוראים את תיאור המשתמש של מרווח המדידה של מדחום הבריאות במכשיר.
בדוגמה הבאה, health_thermometer
הוא השירות Health Thermometer, measurement_interval
הוא המאפיין Measurement Interval ו-gatt.characteristic_user_description
הוא התיאור של המאפיין 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); });
דוגמאות, הדגמות ו-Codelabs
כל הדוגמאות ל-Web Bluetooth שבהמשך נבדקו בהצלחה. כדי ליהנות מהדוגמאות האלה במלואן, מומלץ להתקין את [אפליקציית Android של BLE Peripheral Simulator], שמאפשרת לדמות התקן היקפי מסוג BLE עם שירות סוללה, שירות קצב לב או שירות מדחום בריאות.
רמה למתחילים
- פרטי המכשיר – אחזור פרטי מכשיר בסיסיים ממכשיר BLE.
- רמת הטעינה של הסוללה – אחזור מידע על הסוללה ממכשיר BLE שמפרסם מידע על הסוללה.
- Reset Energy – איפוס האנרגיה שהוצאה ממכשיר BLE שמפרסם את הדופק.
- מאפייני מאפיין – הצגת כל המאפיינים של מאפיין ספציפי ממכשיר BLE.
- Notifications – הפעלה והפסקה של התראות על מאפיינים ממכשיר BLE.
- ניתוק המכשיר – ניתוק של מכשיר BLE לאחר חיבור אליו, וקבלת התראה על הניתוק.
- Get Characteristics – אחזור כל המאפיינים של שירות שפורסם ממכשיר BLE.
- Get Descriptors – אחזור כל מתארי המאפיינים של שירות שפורסם ממכשיר BLE.
- מסנן נתוני היצרן – אחזור פרטי מכשיר בסיסיים ממכשיר BLE שתואמים לנתוני היצרן.
- מסנני החרגה – אחזור מידע בסיסי על מכשיר מ-BLE Device עם מסנני החרגה בסיסיים.
שילוב של כמה פעולות
- GAP Characteristics – הצגת כל מאפייני ה-GAP של מכשיר BLE.
- מאפייני פרטי המכשיר – הצגת כל המאפיינים של פרטי המכשיר של מכשיר BLE.
- Link Loss – הגדרת המאפיין Alert Level של מכשיר BLE (readValue ו-writeValue).
- גילוי שירותים ומאפיינים – גילוי כל השירותים הראשיים הנגישים והמאפיינים שלהם ממכשיר BLE.
- התחברות מחדש אוטומטית – חיבור מחדש למכשיר BLE מנותק באמצעות אלגוריתם חזרה איטית (backoff) מעריכי.
- Read Characteristic Value Changed – קריאת רמת הטעינה של הסוללה וקבלת התראות על שינויים ממכשיר BLE.
- קריאת מתארים – קריאה של כל מתארי המאפיינים של שירות ממכשיר BLE.
- כתיבה של מתאר – כתיבת מתאר 'תיאור משתמש של מאפיין' במכשיר BLE.
מומלץ גם לעיין בהדגמות נבחרות של Web Bluetooth ובCodelabs הרשמיים של Web Bluetooth.
ספריות
- web-bluetooth-utils הוא מודול npm שמוסיף כמה פונקציות נוחות ל-API.
- תוסף ל-Web Bluetooth API זמין ב-noble, המודול המרכזי הפופולרי ביותר של Node.js ל-BLE. כך תוכלו להשתמש ב-Webpack או ב-browserify ל-noble בלי צורך בשרת WebSocket או בפלאגינים אחרים.
- angular-web-bluetooth הוא מודול ל-Angular שמספק רכיב מופשט של כל הקוד הסטנדרטי שנדרש להגדרת ה-Web Bluetooth API.
כלים
- תחילת העבודה עם Web Bluetooth היא אפליקציית אינטרנט פשוטה שתייצר את כל קוד ה-boilerplate של JavaScript כדי להתחיל אינטראקציה עם מכשיר Bluetooth. מזינים שם של מכשיר, שירות או מאפיין, מגדירים את המאפיינים שלו, וזהו.
- אם אתם כבר מפתחים של Bluetooth, Web Bluetooth Developer Studio Plugin יפיק גם את קוד ה-JavaScript של Web Bluetooth למכשיר ה-Bluetooth שלכם.
טיפים
הדף Bluetooth Internals זמין ב-Chrome בכתובת about://bluetooth-internals
, ומאפשר לבדוק את כל הפרטים על מכשירי Bluetooth בקרבת מקום: סטטוס, שירותים, מאפיינים ותיאורים.
מומלץ גם לעיין בדף הרשמי איך שולחים דיווח על באגים ב-Web Bluetooth, כי לפעמים קשה לנפות באגים ב-Bluetooth.
המאמרים הבאים
קודם כדאי לבדוק את סטטוס ההטמעה בדפדפנים ובפלטפורמות כדי לדעת אילו חלקים של Web Bluetooth API מוטמעים כרגע.
התכונה עדיין לא הושלמה, אבל הנה הצצה למה שצפוי בקרוב:
- הסריקה לאיתור מודעות BLE בקרבת מקום תתבצע באמצעות
navigator.bluetooth.requestLEScan()
. - אירוע
serviceadded
חדש יעקוב אחרי שירותי GATT של Bluetooth שהתגלו לאחרונה, ואילו אירועserviceremoved
יעקוב אחרי שירותים שהוסרו. אירועservicechanged
חדש יופעל כשמאפיין או מתאר כלשהו יתווספו או יוסרו משירות Bluetooth GATT.
תמיכה ב-API
האם אתם מתכננים להשתמש ב-Web Bluetooth API? התמיכה הציבורית שלכם עוזרת לצוות Chrome לתת עדיפות לתכונות, ומראה לספקי דפדפנים אחרים כמה חשובה התמיכה שלכם.
אתם יכולים לשלוח ציוץ אל @ChromiumDev באמצעות ההאשטאג #WebBluetooth
ולספר לנו איפה ואיך אתם משתמשים בו.
משאבים
- Stack Overflow
- סטטוס התכונות של Chrome
- באגים בהטמעה של Chrome
- מפרט Web Bluetooth
- דיווח על בעיות במפרט ב-GitHub
- אפליקציית סימולטור של התקן היקפי BLE
תודות
תודה ל-Kayce Basques על בדיקת המאמר. התמונה הראשית (Hero) של SparkFun Electronics מבולדר, ארה"ב.