Web Bluetooth API מאפשר לאתרים לתקשר עם מכשירי Bluetooth.
מה אם נאמר לך שאתרים יכולים לתקשר עם מכשירי Bluetooth בקרבת מקום באופן מאובטח ושומר על פרטיות? בצורה הזו, מוניטורים של הדופק ושירה נורות ואפילו צבים יכולים ליצור אינטראקציה ישירה עם אתר.
עד עכשיו הייתה אפשרות ליצור אינטראקציה עם מכשירי Bluetooth רק לאפליקציות ספציפיות לפלטפורמה. המטרה של Web Bluetooth API היא לשנות מספקת אותו גם בדפדפני אינטרנט.
לפני שנתחיל
המסמך הזה מניח שיש לכם ידע בסיסי על אופן הפעולה של Bluetooth Low ערך האנרגיה (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
.
זמין לגרסאות מקור לניסיון
כדי לקבל משוב רב ככל האפשר ממפתחים המשתמשים באינטרנט Bluetooth API בשדה, Chrome הוסיף בעבר את התכונה הזו ב-Chrome 53 כגרסת מקור לניסיון ל-ChromeOS, ל-Android ול-Mac.
תקופת הניסיון הסתיימה בהצלחה בינואר 2017.
דרישות אבטחה
כדי להבין את ההשפעות שיכולות להיות לכך על האבטחה, אני ממליץ על Web Bluetooth Security פוסט לדוגמה מאת ג'פרי יסקין, מהנדס תוכנה בצוות Chrome, על מפרט ה-Web Bluetooth API.
רק HTTPS
ה-API הניסיוני הזה הוא תכונה חדשה ויעילה שנוספה לאינטרנט, לכן זמין רק להקשרים מאובטחים. כלומר צריך לפתח TLS.
נדרשת תנועת משתמש
כאמצעי אבטחה, איתור מכשירי Bluetooth עם
ההפעלה של navigator.bluetooth.requestDevice
חייבת להתבצע על ידי תנועת משתמש כמו
כנגיעה או כלחיצה בעכבר. אנחנו מדברים על ההאזנה
pointerup
, click
ו-touchend
אירועים.
button.addEventListener('pointerup', function(event) {
// Call navigator.bluetooth.requestDevice
});
כניסה לקוד
Web Bluetooth API מסתמך במידה רבה על Promises של JavaScript. אם אתם לא
כדאי לקרוא את המדריך הנהדר הזה בנושא הבטחות. עוד דבר אחד,
() => {}
הן פונקציות חיצים של 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); });
אם שירות 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); });
אם אתם משתמשים במאפיין Bluetooth GATT מותאם אישית, אפשר לספק את
Bluetooth 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
הכתיבה למאפייני 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
שנוספו
event listener.
התנתקות ממכשיר 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
מתארי 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); });
דגימות, הדגמות ו-Codelabs
כל דוגמאות ה-Bluetooth באינטרנט נבדקו בהצלחה. כדי ליהנות מההטבות האלה אני רוצה שהדגימות יהיו מלאות, מומלץ להתקין את [BLE הציוד ההיקפי סימולטור אפליקציה ל-Android] שמדמה ציוד היקפי מסוג BLE עם שירות סוללה וקצב לב שירות או שירות מדחום בריאותי.
רמה למתחילים
- פרטי מכשיר – אחזור מידע בסיסי על המכשיר ממכשיר BLE.
- רמת הסוללה - אחזור פרטי הסוללה ממידע על הסוללה לפרסום במכשיר BLE.
- איפוס האנרגיה - איפוס האנרגיה שנוצלה ממכשיר BLE המשמש לפרסום דופק.
- מאפיינים – הצגת כל המאפיינים של מאפיין ספציפי ממכשיר BLE.
- התראות – הפעלה והפסקה של התראות אופייניות ממכשיר BLE.
- ניתוק מכשיר – ניתוק מכשיר BLE וקבלת התראה על ניתוק מכשיר BLE אחרי ההתחברות אליו.
- Get Characteristics (קבלת מאפיינים) – גישה לכל המאפיינים של שירות שפורסם ממכשיר BLE.
- Get descriptors (קבלת תיאורים) - get all properties' (קבלת כל המאפיינים) שמתארים שירות שפורסם ממכשיר BLE.
- מסנן הנתונים של היצרן – אחזור פרטים בסיסיים של המכשיר ממכשיר BLE שתואם לנתוני היצרן.
- מסנני החרגה – אחזור פרטים בסיסיים על המכשיר ממכשיר BLE עם מסננים בסיסיים של החרגה.
שילוב של מספר פעולות
- GAP Characteristics (מאפייני GAP) – גישה לכל מאפייני GAP של מכשיר BLE.
- מאפיינים של מידע מהמכשירים שלך – כל המאפיינים של מידע מהמכשירים שלך של מכשיר BLE.
- Link Loss (אובדן קישור) – הגדרת המאפיין של רמת ההתראה למכשיר BLE (readValue &WriteValue).
- גילוי שירותים ו מאפיינים – איתור כל השירותים הראשיים הנגישים והמאפיינים שלהם ממכשיר BLE.
- חיבור מחדש אוטומטי – מחברים מחדש למכשיר BLE מנותק באמצעות אלגוריתם של השהיה מעריכית לפני ניסיון חוזר (exponential backoff).
- קריאת הערך האופייני ששונה – קריאת רמת הטעינה של הסוללה וקבלת התראות על שינויים ממכשיר BLE.
- קריאת תיאוריים – קריאת כל תיאורי המאפיינים של שירות ממכשיר BLE.
- כתיבת תיאור – כותבים למתאר "Characteristic User Description" במכשיר BLE.
כדאי לעיין גם בהדגמות של Bluetooth באינטרנט וגם במעבדות הקוד הרשמיות של Bluetooth באינטרנט.
ספריות
- web-bluetooth-utils הוא מודול NPM שמוסיף פונקציות נוחות ממשק ה-API.
- ספריית shim של Web Bluetooth API זמינה ב-noble, ה-Node.js BLE הפופולרי ביותר מודול מרכזי. כך תוכלו להשתמש ב-webpack/browserify בצורה אצילית בלי שתצטרכו עבור שרת WebSocket או יישומי פלאגין אחרים.
- angular-web-Bluetooth הוא מודול ל-Angular שמפשט את כל נדרש כדי להגדיר את Web Bluetooth API.
כלים
- תחילת העבודה עם Bluetooth באינטרנט היא אפליקציית אינטרנט פשוטה שמפיקה את כל את הקוד הסטנדרטי של JavaScript כדי להתחיל ליצור אינטראקציה עם מכשיר Bluetooth. מזינים את שם המכשיר, השירות והמאפיין, מגדירים את המאפיינים שלו הכול מוכן.
- אם אתם כבר מפתחי Bluetooth, Web Bluetooth Developer Studio הפלאגין גם יפיק את קוד ה-JavaScript של ה-Bluetooth באינטרנט מכשיר Bluetooth.
טיפים
דף Bluetooth Internal זמין ב-Chrome בכתובת
about://bluetooth-internals
כדי שתהיה לך אפשרות לבדוק את כל מה שיש בסביבה
מכשירי Bluetooth: סטטוס, שירותים, מאפיינים ומתארים.
מומלץ גם לעיין במאמר הרשמי איך מגישים באגים באינטרנט ב-Bluetooth כי לפעמים קשה לנפות באגים ב-Bluetooth.
המאמרים הבאים
כדאי לבדוק קודם את סטטוס ההטמעה של הדפדפן והפלטפורמה כדי לדעת אילו חלקים של Web Bluetooth API בהטמעה כרגע.
אמנם התוכנית עדיין לא הושלמה, אבל הנה הצצה למה שצפוי בעתיד עתיד:
- סריקה לאיתור מודעות BLE בקרבת מקום
תתרחש עם
navigator.bluetooth.requestLEScan()
. - אירוע חדש ב-
serviceadded
יעקוב אחר שירותי Bluetooth GATT חדשים שהתגלו ואילו אירועserviceremoved
יעקוב אחר אירועים שהוסרו.servicechanged
חדש האירוע יופעל מיד כשיתווספו מאפיין ו/או מתאר כלשהו, או הוסר משירות Bluetooth GATT.
הצגת תמיכה ב-API
האם בכוונתך להשתמש ב-Web Bluetooth API? התמיכה הציבורית שלך עוזרת לצוות Chrome היא גם מראה לספקי דפדפנים אחרים עד כמה זה חשוב לתמוך בהם.
שליחת ציוץ אל @ChromiumDev בעזרת hashtag
#WebBluetooth
ולהודיע לנו איפה ואיך אתם משתמשים בו.
משאבים
- גלישת מחסנית
- סטטוס התכונות של Chrome
- באגים בהטמעה של Chrome
- מפרט Bluetooth באינטרנט
- מפרטי בעיות ב-GitHub
- אפליקציית סימולטור ציוד היקפי של BLE
אישורים
תודה ל-Kayce Basques על הביקורת הזו. תמונה ראשית (Hero) של SparkFun Electronics מבולדר, ארה"ב.