ה-API של Bluetooth באינטרנט מאפשר לאתרים לתקשר עם מכשירי Bluetooth.
מה יקרה אם אספר לכם שאתרים יכולים לתקשר עם מכשירי Bluetooth בקרבת מקום באופן מאובטח ושומר על הפרטיות? כך, מכשירי מעקב אחר קצב הלב, נורות שרות ואפילו צבים יכולים לקיים אינטראקציה ישירה עם אתר.
עד עכשיו, היכולת לקיים אינטראקציה עם מכשירי Bluetooth הייתה אפשרית רק באפליקציות ספציפיות לפלטפורמה. ה-Web Bluetooth API נועד לשנות את המצב הזה ולהביא אותו גם לדפדפני אינטרנט.
לפני שנתחיל
ההנחה במסמך הזה היא שיש לכם ידע בסיסי באופן שבו פועלים Bluetooth Low Energy (BLE) וGeneric Attribute Profile.
המפרט של Web Bluetooth API עדיין לא הושלם, אבל מחברי המפרט מחפשים באופן פעיל מפתחים נלהבים שינסו את ה-API הזה ויספקו משוב על המפרט ומשוב על ההטמעה.
קבוצת משנה של Web Bluetooth API זמינה ב-ChromeOS, ב-Chrome ל-Android, ב-Mac (Chrome 56) וב-Windows 10 (Chrome 70). כלומר, תוכלו לבקש מכשירי Bluetooth עם צריכת אנרגיה נמוכה בקרבת מקום ולהתחבר אליהם, לקרוא או write מאפייני 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()
מקבלת אובייקט חובה שמגדיר מסננים. המסננים האלה משמשים להחזרת מכשירים שתואמים לשירותי GATT מסוימים של Bluetooth שפורסמו ו/או לשם המכשיר.
מסנן שירותים
לדוגמה, כדי לבקש מכשירי Bluetooth לפרסם את שירות הסוללה של 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
. אפשר גם לספק קידומת נתונים שסננת נתוני יצרן ממכשירי 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.
- התראות – הפעלה והפסקה של התראות על מאפיינים ממכשיר BLE.
- ניתוק המכשיר – ניתוק של מכשיר BLE לאחר החיבור אליו, וקבלת התראה על הניתוק.
- Get Characteristics (קבלת מאפיינים) – גישה לכל המאפיינים של שירות שפורסם ממכשיר BLE.
- קבלת תיאורים – קבלת כל התיאורים של שירות שפורסם ממכשיר BLE.
- מסנן הנתונים של היצרן – אחזור פרטים בסיסיים של המכשיר ממכשיר BLE שתואם לנתוני היצרן.
- מסנני החרגה – אחזור מידע בסיסי על מכשיר מ-BLE Device עם מסנני החרגה בסיסיים.
שילוב של כמה פעולות
- GAP Characteristics – הצגת כל מאפייני ה-GAP של מכשיר BLE.
- מאפייני פרטי המכשיר – הצגת כל המאפיינים של פרטי המכשיר של מכשיר BLE.
- Link Loss – הגדרת המאפיין Alert Level של מכשיר BLE (readValue ו-writeValue).
- גילוי שירותים ומאפיינים – גילוי כל השירותים הראשיים הנגישים והמאפיינים שלהם ממכשיר BLE.
- חיבור מחדש אוטומטי – מחברים מחדש למכשיר BLE מנותק באמצעות אלגוריתם של השהיה מעריכית לפני ניסיון חוזר (exponential backoff).
- Read Characteristic Value Changed – קריאת רמת הטעינה של הסוללה וקבלת התראות על שינויים ממכשיר BLE.
- קריאת מתארים – קריאה של כל מתארי המאפיינים של שירות ממכשיר BLE.
- כתיבת תיאור – כתיבה של המתאר "Characteristic User Description" במכשיר BLE.
כדאי לעיין גם בהדגמות של Bluetooth באינטרנט וגם במעבדות הקוד הרשמיות של 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
ולספר לנו איפה ואיך אתם משתמשים בו.
משאבים
- גלישת מחסנית
- סטטוס התכונות של Chrome
- באגים בהטמעה של Chrome
- מפרט Bluetooth באינטרנט
- דיווח על בעיות במפרט ב-GitHub
- אפליקציית סימולטור של התקן היקפי מסוג BLE
אישורים
תודה ל-Kayce Basques על הביקורת הזו. תמונה ראשית (Hero) של SparkFun Electronics מבולדר, ארה"ב.